From da242452ecd1c838eea0883c2ade752dd882ae67 Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Wed, 23 Feb 2022 12:38:44 +0100 Subject: [PATCH 01/93] start of 1.1.2-DEVELOP --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f4f918..1201a7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "1.1.1", + "version": "1.1.2-DEVELOP", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", From a9a45f795964f2c11caf840840875b8cacc04063 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 7 Mar 2022 13:28:55 +0100 Subject: [PATCH 02/93] doc : maj des serveurs et mail --- documentation/apis/simple/1.0.0/api.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/documentation/apis/simple/1.0.0/api.yaml b/documentation/apis/simple/1.0.0/api.yaml index cf1570a..ba528f4 100644 --- a/documentation/apis/simple/1.0.0/api.yaml +++ b/documentation/apis/simple/1.0.0/api.yaml @@ -4,10 +4,12 @@ info: version: "1.0.0" title: "Service d'itinéraire" contact: - email: "idev@ign.fr" + email: "contact.geoservices@ign.fr" servers: - - url: "https://itineraire.ign.fr/simple/1.0.0/" - description: "Serveur de test IGN" + - url: "https://wxs.ign.fr/calcul/geoportail/itineraire/rest/1.0.0/" + description: "Serveur de test IGN pour l'itinéraire" + - url: "https://wxs.ign.fr/calcul/geoportail/isochrone/rest/1.0.0/" + description: "Serveur de test IGN pour l'isochrone" - url: "http://localhost:8080/simple/1.0.0/" description: "Serveur de test local" tags: From 30d82d2fa3f25d4abd4e1ffe74842704253ed089 Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 18 Mar 2022 14:00:10 +0100 Subject: [PATCH 03/93] test: relecture des tests deja existant --- package.json | 8 ++--- .../simple/1.0.0/controller/controller.js | 2 +- .../configurationComplementTest.feature | 12 ------- .../features/configurationTest.feature | 12 ++++--- test/functional/request/cucumber/data/2.json | 2 +- test/functional/request/cucumber/data/3.json | 2 +- .../cucumber/features/requestDataTest.feature | 34 +++++++++++++------ test/unit/mocha/utils/testsStorageManager.js | 2 +- 8 files changed, 38 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 1201a7b..5f52cd9 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "configCheck": "node ./src/js/road2.js --configCheck", "utest": "mocha --recursive './test/unit/mocha/**/*.js'", "itest": "mocha --recursive './test/integration/mocha/**/*.js'", - "rtest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestTest.feature", - "crtest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestComplementTest.feature", - "drtest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestDataTest.feature", - "artest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestAdminTest.feature", + "rtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestTest.feature", + "crtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestComplementTest.feature", + "drtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestDataTest.feature", + "artest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestAdminTest.feature", "ctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/configurationTest.feature", "cctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/configurationComplementTest.feature", "lint": "eslint -c eslint.json ./src/", diff --git a/src/js/apis/simple/1.0.0/controller/controller.js b/src/js/apis/simple/1.0.0/controller/controller.js index f4dadc4..d5b298f 100644 --- a/src/js/apis/simple/1.0.0/controller/controller.js +++ b/src/js/apis/simple/1.0.0/controller/controller.js @@ -218,7 +218,7 @@ module.exports = { finalIntermediates = this.convertPostArrayToGetParameters(parameters.intermediates, routeOperation.getParameterById("intermediates").serviceParameter, "intermediates"); LOGGER.debug("POST intermediates:"); - LOGGER.debug(parameters.intermediates); + LOGGER.debug(finalIntermediates); } else { finalIntermediates = parameters.intermediates; diff --git a/test/functional/configuration/cucumber/features/configurationComplementTest.feature b/test/functional/configuration/cucumber/features/configurationComplementTest.feature index ec2b4e5..18b1b83 100644 --- a/test/functional/configuration/cucumber/features/configurationComplementTest.feature +++ b/test/functional/configuration/cucumber/features/configurationComplementTest.feature @@ -1,15 +1,3 @@ # Tests fonctionnels de Road2 sur la configuration du serveur pour les cas particuliers suivants: # - problèmes dans la configuration mais on veut que le service démarre quand même -Feature: Road2 configuration - Tests fonctionnels de Road2 sur la configuration du serveur - - Background: - Given I have loaded all my test configuration - -Scenario: [osrm resource] resource absent - Given a valid configuration - And without attribute "resource" in "corse.resource" resource - When I load the server - Then the server log should contain "Les demarrages se sont bien deroules" - diff --git a/test/functional/configuration/cucumber/features/configurationTest.feature b/test/functional/configuration/cucumber/features/configurationTest.feature index 82f5134..1daf995 100644 --- a/test/functional/configuration/cucumber/features/configurationTest.feature +++ b/test/functional/configuration/cucumber/features/configurationTest.feature @@ -405,7 +405,7 @@ Feature: Road2 configuration Scenario: [server.json] (resources.directories sur un dossier qui n'existe pas et en chemin relatif) Given a valid configuration - And with parameter "test" for attribute "application.resources.directories.[0]" in server configuration + And with parameter "test" for attribute "application.resources.directories.[1]" in server configuration When I test the configuration Then the configuration analysis should give an exit code 0 Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas:" @@ -1340,13 +1340,15 @@ Feature: Road2 configuration Scenario: [osrm resource] resource absent Given a valid configuration - And without attribute "resource" in "corse.resource" resource + And without attribute "resource" in "data-osm.resource" resource When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Erreur lors de la lecture de la ressource" + Then the configuration analysis should give an exit code 1 + # TODO : avoir plusieurs ressources dans le dossier pour qu'une d'entre elles marche + # Then the configuration analysis should give an exit code 0 + # Then the server log should contain "Erreur lors de la lecture de la ressource" Scenario: [osrm resource] id different Given a valid configuration - And with parameter "test" for attribute "resource.id" in "corse.resource" resource + And with parameter "test" for attribute "resource.id" in "data-osm.resource" resource When I test the configuration Then the configuration analysis should give an exit code 0 diff --git a/test/functional/request/cucumber/data/2.json b/test/functional/request/cucumber/data/2.json index 86166ff..a0083bf 100644 --- a/test/functional/request/cucumber/data/2.json +++ b/test/functional/request/cucumber/data/2.json @@ -1 +1 @@ -{"resource":"bdtopo-pgr","resourceVersion":"2020-09-28","start":"3.818441,49.081297","end":"3.847338,49.083684","profile":"car","optimization":"shortest","geometry":{"type":"LineString","coordinates":[[3.818441,49.081297],[3.818461,49.081299],[3.818745,49.081328],[3.819184,49.081377],[3.81969,49.081443],[3.820034,49.081486],[3.820113,49.081497],[3.820449,49.081545],[3.820537,49.081557],[3.820779,49.08159],[3.821035,49.081633],[3.821098,49.081646],[3.821215,49.081672],[3.821351,49.081708],[3.821538,49.081768],[3.821834,49.081871],[3.822306,49.082038],[3.822916,49.082256],[3.822938,49.082264],[3.822965,49.082273],[3.823033,49.082298],[3.823248,49.082375],[3.823867,49.082589],[3.823901,49.082601],[3.824389,49.082776],[3.824803,49.082931],[3.825105,49.083056],[3.825282,49.083139],[3.825447,49.08323],[3.825685,49.083355],[3.825787,49.083416],[3.825963,49.08352],[3.826084,49.083588],[3.82615,49.083624],[3.826436,49.083796],[3.826597,49.083889],[3.826823,49.08402],[3.826927,49.084079],[3.826927,49.084079],[3.82702,49.084133],[3.827145,49.084213],[3.827216,49.084257],[3.827373,49.084377],[3.827529,49.084512],[3.827653,49.084633],[3.82779,49.084784],[3.827823,49.084819],[3.827974,49.084985],[3.828195,49.085215],[3.828298,49.085318],[3.828415,49.085424],[3.828498,49.085487],[3.828535,49.08551],[3.828595,49.085547],[3.828698,49.085591],[3.828803,49.085632],[3.828928,49.085678],[3.828962,49.085688],[3.829224,49.085763],[3.829261,49.085772],[3.829569,49.085851],[3.830101,49.085987],[3.830571,49.086138],[3.830614,49.086152],[3.830676,49.086164],[3.830735,49.086187],[3.830786,49.086216],[3.830825,49.086241],[3.830854,49.086268],[3.830981,49.086256],[3.831069,49.086209],[3.831103,49.086085],[3.831105,49.086035],[3.831091,49.085941],[3.831079,49.085808],[3.831101,49.085709],[3.831134,49.085544],[3.831154,49.085516],[3.831189,49.085471],[3.831221,49.085445],[3.831295,49.085417],[3.831379,49.085402],[3.831444,49.085395],[3.831598,49.085374],[3.831767,49.085351],[3.831924,49.085327],[3.83211,49.085298],[3.832166,49.085287],[3.832475,49.085228],[3.832738,49.085168],[3.833038,49.085088],[3.833334,49.085011],[3.833426,49.084988],[3.833748,49.084889],[3.833912,49.084827],[3.833963,49.084817],[3.834028,49.084802],[3.834155,49.084791],[3.83431,49.08477],[3.834359,49.084762],[3.834495,49.084739],[3.834624,49.0847],[3.834796,49.084603],[3.834983,49.084513],[3.835017,49.084495],[3.835073,49.084472],[3.835114,49.084456],[3.835194,49.08442],[3.835371,49.084375],[3.835431,49.084358],[3.835591,49.084315],[3.835748,49.084279],[3.835869,49.084256],[3.835933,49.084253],[3.835965,49.084253],[3.835967,49.084213],[3.83598,49.084117],[3.836,49.084045],[3.836021,49.08399],[3.836051,49.083929],[3.836097,49.083867],[3.836165,49.083785],[3.836219,49.083737],[3.836279,49.083684],[3.836398,49.083595],[3.836547,49.083495],[3.836571,49.083478],[3.836571,49.083478],[3.836716,49.083376],[3.836848,49.083278],[3.836982,49.083143],[3.837105,49.082991],[3.837189,49.082829],[3.837261,49.082694],[3.83731,49.08261],[3.837354,49.082538],[3.837436,49.082419],[3.837539,49.0823],[3.837571,49.082273],[3.837621,49.08223],[3.837724,49.082163],[3.837838,49.082098],[3.837932,49.082052],[3.838048,49.082011],[3.838138,49.081985],[3.838247,49.081957],[3.83842,49.081927],[3.838591,49.081908],[3.838775,49.081895],[3.838764,49.081636],[3.838746,49.081505],[3.838749,49.081467],[3.838771,49.081435],[3.838803,49.081418],[3.838906,49.081397],[3.839109,49.081368],[3.839172,49.081358],[3.839476,49.081302],[3.839797,49.081234],[3.839924,49.081207],[3.840354,49.081129],[3.840692,49.081053],[3.840727,49.081045],[3.841322,49.080895],[3.841424,49.080868],[3.841479,49.080855],[3.841532,49.080844],[3.841653,49.080838],[3.841801,49.080839],[3.842051,49.080844],[3.842238,49.08085],[3.842247,49.080817],[3.842278,49.080704],[3.842329,49.080606],[3.842387,49.080512],[3.842479,49.080411],[3.842628,49.080271],[3.842806,49.080135],[3.842991,49.080007],[3.843052,49.079965],[3.843431,49.079698],[3.843711,49.079492],[3.843835,49.079402],[3.844178,49.079159],[3.844303,49.079071],[3.844303,49.079071],[3.844178,49.079159],[3.843835,49.079402],[3.843711,49.079492],[3.843431,49.079698],[3.843052,49.079965],[3.842991,49.080007],[3.842806,49.080135],[3.842628,49.080271],[3.842479,49.080411],[3.842387,49.080512],[3.842329,49.080606],[3.842278,49.080704],[3.842247,49.080817],[3.842238,49.08085],[3.842051,49.080844],[3.841801,49.080839],[3.841653,49.080838],[3.841532,49.080844],[3.841479,49.080855],[3.841424,49.080868],[3.841322,49.080895],[3.840727,49.081045],[3.840692,49.081053],[3.840354,49.081129],[3.839924,49.081207],[3.839797,49.081234],[3.839476,49.081302],[3.839172,49.081358],[3.839109,49.081368],[3.838906,49.081397],[3.838803,49.081418],[3.838771,49.081435],[3.838749,49.081467],[3.838746,49.081505],[3.838764,49.081636],[3.838775,49.081895],[3.838633,49.081836],[3.838298,49.081747],[3.838119,49.081701],[3.837844,49.081628],[3.83759,49.081547],[3.837467,49.081514],[3.837275,49.081492],[3.837214,49.081486],[3.837045,49.081472],[3.836823,49.081464],[3.836746,49.081453],[3.836683,49.081443],[3.836354,49.081965],[3.836257,49.082076],[3.836139,49.082139],[3.835806,49.082207],[3.835284,49.082288],[3.835146,49.08231],[3.834731,49.082354],[3.834381,49.082406],[3.834245,49.082447],[3.834184,49.082484],[3.834145,49.082533],[3.834116,49.082583],[3.83411,49.082633],[3.834118,49.082699],[3.83414,49.082751],[3.834291,49.083026],[3.834429,49.083254],[3.834469,49.083373],[3.834466,49.083429],[3.834423,49.083485],[3.834328,49.083543],[3.834074,49.083648],[3.833836,49.083741],[3.833707,49.083777],[3.833611,49.083794],[3.833473,49.083806],[3.833384,49.083809],[3.833264,49.083811],[3.832843,49.083813],[3.83242,49.083796],[3.83209,49.083771],[3.832035,49.083764],[3.831976,49.083756],[3.83194,49.08375],[3.831858,49.083739],[3.831778,49.08373],[3.831566,49.083713],[3.830829,49.08366],[3.83032,49.083608],[3.830201,49.083601],[3.830212,49.083571],[3.830237,49.083326],[3.830242,49.083287],[3.830268,49.083122],[3.830278,49.083038],[3.830258,49.08292],[3.830209,49.082759],[3.830184,49.082671],[3.830093,49.082424],[3.830052,49.082345],[3.83003,49.082326],[3.829982,49.082282],[3.829895,49.082228],[3.829777,49.082207],[3.829628,49.0822],[3.82941,49.082205],[3.829187,49.082218],[3.829105,49.082221],[3.828895,49.082229],[3.828454,49.082244],[3.828454,49.082244],[3.828895,49.082229],[3.829105,49.082221],[3.829187,49.082218],[3.82941,49.082205],[3.829628,49.0822],[3.829777,49.082207],[3.829895,49.082228],[3.829982,49.082282],[3.83003,49.082326],[3.830052,49.082345],[3.830093,49.082424],[3.830184,49.082671],[3.830209,49.082759],[3.830258,49.08292],[3.830278,49.083038],[3.830268,49.083122],[3.830242,49.083287],[3.830237,49.083326],[3.830212,49.083571],[3.830201,49.083601],[3.83032,49.083608],[3.830829,49.08366],[3.831566,49.083713],[3.831778,49.08373],[3.831858,49.083739],[3.83194,49.08375],[3.831976,49.083756],[3.832035,49.083764],[3.83209,49.083771],[3.83242,49.083796],[3.832843,49.083813],[3.833264,49.083811],[3.833384,49.083809],[3.833473,49.083806],[3.833611,49.083794],[3.833707,49.083777],[3.833836,49.083741],[3.834074,49.083648],[3.834328,49.083543],[3.834423,49.083485],[3.834466,49.083429],[3.834469,49.083373],[3.834429,49.083254],[3.834291,49.083026],[3.83414,49.082751],[3.834118,49.082699],[3.83411,49.082633],[3.834116,49.082583],[3.834145,49.082533],[3.834184,49.082484],[3.834245,49.082447],[3.834381,49.082406],[3.834731,49.082354],[3.835146,49.08231],[3.835284,49.082288],[3.835806,49.082207],[3.836139,49.082139],[3.836257,49.082076],[3.836354,49.081965],[3.836683,49.081443],[3.836746,49.081453],[3.836823,49.081464],[3.837045,49.081472],[3.837214,49.081486],[3.837275,49.081492],[3.837467,49.081514],[3.83759,49.081547],[3.837844,49.081628],[3.838119,49.081701],[3.838298,49.081747],[3.838633,49.081836],[3.838775,49.081895],[3.838875,49.081889],[3.839348,49.081854],[3.839414,49.081849],[3.839745,49.081829],[3.840027,49.08181],[3.840343,49.081797],[3.840575,49.081792],[3.840769,49.081787],[3.840938,49.081784],[3.841079,49.081774],[3.841185,49.081761],[3.841316,49.081736],[3.841342,49.081727],[3.841477,49.081687],[3.841747,49.08161],[3.841786,49.081599],[3.842034,49.081519],[3.842151,49.081476],[3.842177,49.081447],[3.842217,49.08146],[3.842264,49.08149],[3.842448,49.081714],[3.842725,49.082069],[3.842814,49.082145],[3.842944,49.082265],[3.84313,49.082441],[3.843226,49.082526],[3.843422,49.082681],[3.843632,49.082864],[3.843684,49.082919],[3.843749,49.083007],[3.8438,49.083112],[3.843855,49.083297],[3.843932,49.083499],[3.84399,49.083638],[3.844061,49.083789],[3.84413,49.083879],[3.84424,49.083993],[3.844374,49.0841],[3.844616,49.084238],[3.844774,49.084327],[3.844905,49.0843],[3.845309,49.084164],[3.845546,49.084092],[3.845922,49.083996],[3.846568,49.083856],[3.846689,49.083829],[3.846761,49.083814],[3.847138,49.083724],[3.847338,49.083684]]},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[3.818441,49.079071,3.847338,49.086268],"distance":6110.2,"duration":99.93166666666666,"constraints":[],"portions":[{"start":"3.818441,49.081297","end":"3.826922,49.084083","distance":705.5,"duration":0.6716666666666666,"bbox":[3.818441,49.081297,3.82692723197936,49.084079134697696],"steps":[{"geometry":{"type":"LineString","coordinates":[[3.818441,49.081297],[3.818461,49.081299],[3.818745,49.081328],[3.819184,49.081377],[3.81969,49.081443],[3.820034,49.081486],[3.820113,49.081497],[3.820449,49.081545],[3.820537,49.081557]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":155.8,"duration":0.14833333333333334,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.820779,49.08159],[3.821035,49.081633],[3.821098,49.081646],[3.821215,49.081672],[3.821351,49.081708],[3.821538,49.081768],[3.821834,49.081871],[3.822306,49.082038],[3.822916,49.082256]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":191.5,"duration":0.18166666666666667,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.822938,49.082264],[3.822965,49.082273],[3.823033,49.082298]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":9.7,"duration":0.01,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.823248,49.082375],[3.823867,49.082589],[3.823901,49.082601],[3.824389,49.082776],[3.824803,49.082931],[3.825105,49.083056],[3.825282,49.083139],[3.825447,49.08323],[3.825685,49.083355],[3.825787,49.083416],[3.825963,49.08352],[3.826084,49.083588]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":266.2,"duration":0.2533333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.82615,49.083624],[3.826436,49.083796],[3.826597,49.083889]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":50.3,"duration":0.04833333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.826823,49.08402],[3.826927,49.084079]]},"attributes":{"nom_1_gauche":"GRANDE RUE","nom_1_droite":"GRANDE RUE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":32,"duration":0.030000000000000002,"instruction":""}]},{"start":"3.826922,49.084083","end":"3.83657,49.083477","distance":969.6,"duration":1.4016666666666666,"bbox":[3.82692723197936,49.083477790843915,3.8365712973297255,49.086268],"steps":[{"geometry":{"type":"LineString","coordinates":[[3.826927,49.084079],[3.82702,49.084133],[3.827145,49.084213],[3.827216,49.084257],[3.827373,49.084377],[3.827529,49.084512],[3.827653,49.084633],[3.82779,49.084784],[3.827823,49.084819],[3.827974,49.084985],[3.828195,49.085215],[3.828298,49.085318],[3.828415,49.085424],[3.828498,49.085487],[3.828535,49.08551]]},"attributes":{"nom_1_gauche":"GRANDE RUE","nom_1_droite":"GRANDE RUE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":198.8,"duration":0.19,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.828595,49.085547],[3.828698,49.085591]]},"attributes":{"nom_1_gauche":"GRANDE RUE","nom_1_droite":"GRANDE RUE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":15,"duration":0.015000000000000001,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.828803,49.085632],[3.828928,49.085678],[3.828962,49.085688],[3.829224,49.085763],[3.829261,49.085772],[3.829569,49.085851],[3.830101,49.085987],[3.830571,49.086138]]},"attributes":{"nom_1_gauche":"GRANDE RUE","nom_1_droite":"GRANDE RUE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":150,"duration":0.2,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.830614,49.086152],[3.830676,49.086164],[3.830735,49.086187],[3.830786,49.086216],[3.830825,49.086241],[3.830854,49.086268]]},"attributes":{"nom_1_gauche":"GRANDE RUE","nom_1_droite":"GRANDE RUE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":25.8,"duration":0.035,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.830981,49.086256],[3.831069,49.086209],[3.831103,49.086085],[3.831105,49.086035],[3.831091,49.085941],[3.831079,49.085808],[3.831101,49.085709],[3.831134,49.085544],[3.831154,49.085516],[3.831189,49.085471],[3.831221,49.085445],[3.831295,49.085417],[3.831379,49.085402],[3.831444,49.085395],[3.831598,49.085374],[3.831767,49.085351],[3.831924,49.085327]]},"attributes":{"nom_1_gauche":"R DES VIGNES","nom_1_droite":"R DES VIGNES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":158.3,"duration":0.31666666666666665,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.83211,49.085298],[3.832166,49.085287],[3.832475,49.085228],[3.832738,49.085168],[3.833038,49.085088],[3.833334,49.085011],[3.833426,49.084988],[3.833748,49.084889],[3.833912,49.084827],[3.833963,49.084817]]},"attributes":{"nom_1_gauche":"R DES VIGNES","nom_1_droite":"R DES VIGNES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":159.8,"duration":0.2733333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.834028,49.084802],[3.834155,49.084791],[3.83431,49.08477],[3.834359,49.084762],[3.834495,49.084739],[3.834624,49.0847],[3.834796,49.084603],[3.834983,49.084513],[3.835017,49.084495],[3.835073,49.084472],[3.835114,49.084456],[3.835194,49.08442],[3.835371,49.084375],[3.835431,49.084358],[3.835591,49.084315],[3.835748,49.084279],[3.835869,49.084256],[3.835933,49.084253],[3.835965,49.084253]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":162,"duration":0.2783333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.835967,49.084213],[3.83598,49.084117],[3.836,49.084045],[3.836021,49.08399],[3.836051,49.083929],[3.836097,49.083867],[3.836165,49.083785],[3.836219,49.083737],[3.836279,49.083684],[3.836398,49.083595],[3.836547,49.083495],[3.836571,49.083478]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":100.1,"duration":0.095,"instruction":""}]},{"start":"3.83657,49.083477","end":"3.844303,49.079071","distance":823.4,"duration":1.2416666666666667,"bbox":[3.8365712973297255,49.079071,3.844303,49.083477790843915],"steps":[{"geometry":{"type":"LineString","coordinates":[[3.836571,49.083478],[3.836716,49.083376],[3.836848,49.083278],[3.836982,49.083143],[3.837105,49.082991],[3.837189,49.082829],[3.837261,49.082694],[3.83731,49.08261]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":112.1,"duration":0.10666666666666667,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.837354,49.082538],[3.837436,49.082419],[3.837539,49.0823],[3.837571,49.082273],[3.837621,49.08223],[3.837724,49.082163],[3.837838,49.082098],[3.837932,49.082052],[3.838048,49.082011],[3.838138,49.081985],[3.838247,49.081957],[3.83842,49.081927],[3.838591,49.081908],[3.838775,49.081895]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":143,"duration":0.19,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838764,49.081636],[3.838746,49.081505],[3.838749,49.081467],[3.838771,49.081435]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":51.5,"duration":0.10333333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838803,49.081418],[3.838906,49.081397],[3.839109,49.081368]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":26.1,"duration":0.051666666666666666,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.839172,49.081358],[3.839476,49.081302],[3.839797,49.081234],[3.839924,49.081207],[3.840354,49.081129],[3.840692,49.081053],[3.840727,49.081045],[3.841322,49.080895],[3.841424,49.080868],[3.841479,49.080855]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":182.4,"duration":0.365,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.841532,49.080844],[3.841653,49.080838],[3.841801,49.080839],[3.842051,49.080844],[3.842238,49.08085]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":55.7,"duration":0.11166666666666666,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.842247,49.080817],[3.842278,49.080704],[3.842329,49.080606],[3.842387,49.080512],[3.842479,49.080411],[3.842628,49.080271],[3.842806,49.080135],[3.842991,49.080007],[3.843052,49.079965],[3.843431,49.079698],[3.843711,49.079492]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":188.7,"duration":0.25166666666666665,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.843835,49.079402],[3.844178,49.079159],[3.844303,49.079071]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":63.8,"duration":0.060000000000000005,"instruction":""}]},{"start":"3.844303,49.079071","end":"3.828454,49.082244","distance":1670.7,"duration":42.20666666666667,"bbox":[3.828454,49.079071,3.844303,49.083813],"steps":[{"geometry":{"type":"LineString","coordinates":[[3.844303,49.079071],[3.844178,49.079159],[3.843835,49.079402],[3.843711,49.079492]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":63.8,"duration":0.060000000000000005,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.843431,49.079698],[3.843052,49.079965],[3.842991,49.080007],[3.842806,49.080135],[3.842628,49.080271],[3.842479,49.080411],[3.842387,49.080512],[3.842329,49.080606],[3.842278,49.080704],[3.842247,49.080817],[3.842238,49.08085]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":188.7,"duration":0.25166666666666665,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.842051,49.080844],[3.841801,49.080839],[3.841653,49.080838],[3.841532,49.080844],[3.841479,49.080855]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":55.7,"duration":0.11166666666666666,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.841424,49.080868],[3.841322,49.080895],[3.840727,49.081045],[3.840692,49.081053],[3.840354,49.081129],[3.839924,49.081207],[3.839797,49.081234],[3.839476,49.081302],[3.839172,49.081358],[3.839109,49.081368]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":182.4,"duration":0.365,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838906,49.081397],[3.838803,49.081418],[3.838771,49.081435]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":26.1,"duration":0.051666666666666666,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838749,49.081467],[3.838746,49.081505],[3.838764,49.081636],[3.838775,49.081895]]},"attributes":{"nom_1_gauche":"R DES FOSSES","nom_1_droite":"R DES FOSSES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":51.5,"duration":0.10333333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838633,49.081836],[3.838298,49.081747],[3.838119,49.081701]]},"attributes":{"nom_1_gauche":"R DES CRAYERES","nom_1_droite":"R DES CRAYERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":52.7,"duration":0.105,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.837844,49.081628],[3.83759,49.081547],[3.837467,49.081514],[3.837275,49.081492],[3.837214,49.081486],[3.837045,49.081472],[3.836823,49.081464],[3.836746,49.081453],[3.836683,49.081443]]},"attributes":{"nom_1_gauche":"R DES CRAYERES","nom_1_droite":"R DES CRAYERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":109.9,"duration":0.22,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.836354,49.081965],[3.836257,49.082076],[3.836139,49.082139],[3.835806,49.082207],[3.835284,49.082288],[3.835146,49.08231],[3.834731,49.082354],[3.834381,49.082406],[3.834245,49.082447],[3.834184,49.082484],[3.834145,49.082533],[3.834116,49.082583],[3.83411,49.082633],[3.834118,49.082699],[3.83414,49.082751],[3.834291,49.083026],[3.834429,49.083254],[3.834469,49.083373],[3.834466,49.083429],[3.834423,49.083485],[3.834328,49.083543],[3.834074,49.083648],[3.833836,49.083741],[3.833707,49.083777],[3.833611,49.083794],[3.833473,49.083806]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":434.1,"duration":26.045,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.833384,49.083809],[3.833264,49.083811],[3.832843,49.083813],[3.83242,49.083796],[3.83209,49.083771],[3.832035,49.083764],[3.831976,49.083756],[3.83194,49.08375],[3.831858,49.083739],[3.831778,49.08373],[3.831566,49.083713],[3.830829,49.08366],[3.83032,49.083608],[3.830201,49.083601]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":240.6,"duration":14.438333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.830212,49.083571],[3.830237,49.083326],[3.830242,49.083287],[3.830268,49.083122],[3.830278,49.083038]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":62.9,"duration":0.10833333333333334,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.830258,49.08292],[3.830209,49.082759],[3.830184,49.082671],[3.830093,49.082424],[3.830052,49.082345],[3.83003,49.082326],[3.829982,49.082282],[3.829895,49.082228],[3.829777,49.082207],[3.829628,49.0822],[3.82941,49.082205],[3.829187,49.082218],[3.829105,49.082221],[3.828895,49.082229],[3.828454,49.082244]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":202.2,"duration":0.3466666666666667,"instruction":""}]},{"start":"3.828454,49.082244","end":"3.847338,49.083684","distance":1940.9,"duration":54.41,"bbox":[3.828454,49.081443,3.847338,49.084327],"steps":[{"geometry":{"type":"LineString","coordinates":[[3.828454,49.082244],[3.828895,49.082229],[3.829105,49.082221],[3.829187,49.082218],[3.82941,49.082205],[3.829628,49.0822],[3.829777,49.082207],[3.829895,49.082228],[3.829982,49.082282],[3.83003,49.082326],[3.830052,49.082345],[3.830093,49.082424],[3.830184,49.082671],[3.830209,49.082759],[3.830258,49.08292],[3.830278,49.083038]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":202.2,"duration":0.3466666666666667,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.830268,49.083122],[3.830242,49.083287],[3.830237,49.083326],[3.830212,49.083571],[3.830201,49.083601]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":62.9,"duration":0.10833333333333334,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.83032,49.083608],[3.830829,49.08366],[3.831566,49.083713],[3.831778,49.08373],[3.831858,49.083739],[3.83194,49.08375],[3.831976,49.083756],[3.832035,49.083764],[3.83209,49.083771],[3.83242,49.083796],[3.832843,49.083813],[3.833264,49.083811],[3.833384,49.083809],[3.833473,49.083806]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":240.6,"duration":14.438333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.833611,49.083794],[3.833707,49.083777],[3.833836,49.083741],[3.834074,49.083648],[3.834328,49.083543],[3.834423,49.083485],[3.834466,49.083429],[3.834469,49.083373],[3.834429,49.083254],[3.834291,49.083026],[3.83414,49.082751],[3.834118,49.082699],[3.83411,49.082633],[3.834116,49.082583],[3.834145,49.082533],[3.834184,49.082484],[3.834245,49.082447],[3.834381,49.082406],[3.834731,49.082354],[3.835146,49.08231],[3.835284,49.082288],[3.835806,49.082207],[3.836139,49.082139],[3.836257,49.082076],[3.836354,49.081965],[3.836683,49.081443]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":434.1,"duration":26.045,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.836746,49.081453],[3.836823,49.081464],[3.837045,49.081472],[3.837214,49.081486],[3.837275,49.081492],[3.837467,49.081514],[3.83759,49.081547],[3.837844,49.081628],[3.838119,49.081701]]},"attributes":{"nom_1_gauche":"R DES CRAYERES","nom_1_droite":"R DES CRAYERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":109.9,"duration":0.22,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838298,49.081747],[3.838633,49.081836],[3.838775,49.081895]]},"attributes":{"nom_1_gauche":"R DES CRAYERES","nom_1_droite":"R DES CRAYERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":52.7,"duration":0.105,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.838875,49.081889]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":""},"distance":7.3,"duration":0.01,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.839348,49.081854]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":34.8,"duration":0.04666666666666666,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.839414,49.081849],[3.839745,49.081829],[3.840027,49.08181],[3.840343,49.081797],[3.840575,49.081792],[3.840769,49.081787],[3.840938,49.081784],[3.841079,49.081774],[3.841185,49.081761],[3.841316,49.081736],[3.841342,49.081727]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":146.8,"duration":0.19499999999999998,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.841477,49.081687],[3.841747,49.08161]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":32.3,"duration":0.043333333333333335,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.841786,49.081599],[3.842034,49.081519],[3.842151,49.081476],[3.842177,49.081447]]},"attributes":{"nom_1_gauche":"R DE CHAMPAGNE","nom_1_droite":"R DE CHAMPAGNE","cpx_numero":"D1","cpx_toponyme_route_nommee":"Route Touristique du Champagne de la Vallée de la Marne"},"distance":36.8,"duration":0.04833333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.842217,49.08146],[3.842264,49.08149],[3.842448,49.081714]]},"attributes":{"nom_1_gauche":"R DU BANC DE PIERRE","nom_1_droite":"R DU BANC DE PIERRE","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":36.4,"duration":0.07333333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.842725,49.082069]]},"attributes":{"nom_1_gauche":"R DU BANC DE PIERRE","nom_1_droite":"R DU BANC DE PIERRE","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":44.4,"duration":0.08833333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.842814,49.082145],[3.842944,49.082265],[3.84313,49.082441]]},"attributes":{"nom_1_gauche":"R DU FBG D ARNOTAY","nom_1_droite":"R DU FBG D ARNOTAY","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":50.8,"duration":0.10166666666666666,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.843226,49.082526],[3.843422,49.082681],[3.843632,49.082864],[3.843684,49.082919],[3.843749,49.083007],[3.8438,49.083112],[3.843855,49.083297],[3.843932,49.083499],[3.84399,49.083638],[3.844061,49.083789],[3.84413,49.083879],[3.84424,49.083993],[3.844374,49.0841]]},"attributes":{"nom_1_gauche":"R DU FBG D ARNOTAY","nom_1_droite":"R DU FBG D ARNOTAY","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":209.4,"duration":0.41833333333333333,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.844616,49.084238],[3.844774,49.084327]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":38.6,"duration":0.06666666666666667,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.844905,49.0843],[3.845309,49.084164],[3.845546,49.084092],[3.845922,49.083996],[3.846568,49.083856],[3.846689,49.083829],[3.846761,49.083814],[3.847138,49.083724],[3.847338,49.083684]]},"attributes":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":200.9,"duration":12.055,"instruction":""}]}]} +{"resource":"bdtopo-pgr","resourceVersion":"2022-01-24","start":"2.557209,48.742584","end":"2.561383,48.741514","profile":"car","optimization":"fastest","geometry":{"type":"LineString","coordinates":[[2.557209,48.742584],[2.557236,48.742585],[2.557472,48.742585],[2.557542,48.742582],[2.557592,48.74258],[2.557654,48.742573],[2.557708,48.742566],[2.557769,48.742555],[2.557833,48.742542],[2.557913,48.742522],[2.557991,48.742499],[2.558086,48.742473],[2.55818,48.742452],[2.558281,48.742434],[2.558353,48.742427],[2.558445,48.742418],[2.558534,48.742415],[2.558534,48.742352],[2.558541,48.742295],[2.558554,48.742214],[2.558553,48.742145],[2.558551,48.742101],[2.558536,48.742056],[2.558509,48.742012],[2.558476,48.74197],[2.558424,48.741924],[2.558397,48.741907],[2.558397,48.741907],[2.558424,48.741924],[2.558476,48.74197],[2.558509,48.742012],[2.558536,48.742056],[2.558551,48.742101],[2.558553,48.742145],[2.558554,48.742214],[2.558541,48.742295],[2.558534,48.742352],[2.558534,48.742415],[2.558658,48.742411],[2.558744,48.742411],[2.558853,48.742409],[2.558956,48.742411],[2.559256,48.742421],[2.559396,48.742424],[2.559537,48.742427],[2.559794,48.742423],[2.559913,48.742421],[2.560004,48.742414],[2.560105,48.742407],[2.560166,48.742398],[2.560226,48.742391],[2.560308,48.742377],[2.560395,48.74236],[2.560497,48.742335],[2.56059,48.742309],[2.560763,48.742253],[2.560812,48.742228],[2.560862,48.742203],[2.560952,48.742152],[2.561031,48.742097],[2.561104,48.742034],[2.561167,48.741969],[2.561218,48.741912],[2.561259,48.741852],[2.561289,48.741792],[2.561317,48.741737],[2.561348,48.741648],[2.561373,48.741568],[2.561384,48.741514]]},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.557209,48.741514,2.561384,48.742585],"distance":484.4,"duration":0.7866666666666667,"constraints":[],"portions":[{"start":"2.557209,48.742584","end":"2.558397,48.741907","distance":160.1,"duration":0.26999999999999996,"bbox":[2.557209,48.741907,2.558554,48.742585],"steps":[{"geometry":{"type":"LineString","coordinates":[[2.557209,48.742584],[2.557236,48.742585],[2.557472,48.742585],[2.557542,48.742582],[2.557592,48.74258],[2.557654,48.742573],[2.557708,48.742566],[2.557769,48.742555],[2.557833,48.742542],[2.557913,48.742522]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":52.7,"duration":0.07833333333333334,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.557991,48.742499],[2.558086,48.742473],[2.55818,48.742452],[2.558281,48.742434],[2.558353,48.742427],[2.558445,48.742418],[2.558534,48.742415]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":47.6,"duration":0.07166666666666667,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.558534,48.742352],[2.558541,48.742295],[2.558554,48.742214],[2.558553,48.742145],[2.558551,48.742101],[2.558536,48.742056],[2.558509,48.742012],[2.558476,48.74197],[2.558424,48.741924],[2.558397,48.741907]]},"attributes":{"nom_1_gauche":"R DES AUBEPINES","nom_1_droite":"R DES AUBEPINES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":59.8,"duration":0.12000000000000001,"instruction":{}}]},{"start":"2.558397,48.741907","end":"2.561383,48.741514","distance":324.3,"duration":0.5166666666666667,"bbox":[2.558397,48.741514086987735,2.561383982280277,48.742427],"steps":[{"geometry":{"type":"LineString","coordinates":[[2.558397,48.741907],[2.558424,48.741924],[2.558476,48.74197],[2.558509,48.742012],[2.558536,48.742056],[2.558551,48.742101],[2.558553,48.742145],[2.558554,48.742214],[2.558541,48.742295],[2.558534,48.742352],[2.558534,48.742415]]},"attributes":{"nom_1_gauche":"R DES AUBEPINES","nom_1_droite":"R DES AUBEPINES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":59.8,"duration":0.12000000000000001,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.558658,48.742411],[2.558744,48.742411],[2.558853,48.742409],[2.558956,48.742411],[2.559256,48.742421],[2.559396,48.742424],[2.559537,48.742427]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":73.8,"duration":0.11,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.559794,48.742423],[2.559913,48.742421],[2.560004,48.742414]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":34.5,"duration":0.051666666666666666,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560105,48.742407]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":7.5,"duration":0.011666666666666665,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560166,48.742398],[2.560226,48.742391]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":9.1,"duration":0.013333333333333334,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560308,48.742377]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":6.2,"duration":0.01,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560395,48.74236],[2.560497,48.742335],[2.56059,48.742309],[2.560763,48.742253]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":36.3,"duration":0.055,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560812,48.742228],[2.560862,48.742203],[2.560952,48.742152],[2.561031,48.742097],[2.561104,48.742034],[2.561167,48.741969],[2.561218,48.741912],[2.561259,48.741852],[2.561289,48.741792]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":65.4,"duration":0.09833333333333334,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.561317,48.741737],[2.561348,48.741648],[2.561373,48.741568],[2.561384,48.741514]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":31.8,"duration":0.04833333333333333,"instruction":{}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/data/3.json b/test/functional/request/cucumber/data/3.json index fbc863b..f7e25b7 100644 --- a/test/functional/request/cucumber/data/3.json +++ b/test/functional/request/cucumber/data/3.json @@ -1 +1 @@ -{"resource":"bdtopo-pgr","resourceVersion":"2020-09-28","start":"3.337845,43.310028","end":"3.339036,43.310531","profile":"car","optimization":"fastest","geometry":{"type":"LineString","coordinates":[[3.337845,43.310027],[3.337827,43.310028],[3.337773,43.310036],[3.337727,43.310054],[3.337699,43.310081],[3.337688,43.310114],[3.337693,43.310148],[3.33771,43.31018],[3.337736,43.310207],[3.33778,43.310225],[3.337838,43.310231],[3.337902,43.310229],[3.337961,43.310226],[3.338012,43.310225],[3.338057,43.310225],[3.338059,43.310229],[3.338062,43.310243],[3.338071,43.310265],[3.338077,43.310306],[3.338068,43.310363],[3.33805,43.310431],[3.338031,43.310506],[3.338012,43.310572],[3.338127,43.310571],[3.339036,43.310531]]},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[3.337688,43.310027,3.339036,43.310572],"distance":178,"duration":0.305,"constraints":[],"portions":[{"start":"3.337845,43.310028","end":"3.339036,43.310531","distance":178,"duration":0.305,"bbox":[3.337688,43.310027,3.339036,43.310572],"steps":[{"geometry":{"type":"LineString","coordinates":[[3.337845,43.310027],[3.337827,43.310028],[3.337773,43.310036],[3.337727,43.310054],[3.337699,43.310081],[3.337688,43.310114],[3.337693,43.310148],[3.33771,43.31018],[3.337736,43.310207],[3.33778,43.310225],[3.337838,43.310231],[3.337902,43.310229],[3.337961,43.310226],[3.338012,43.310225],[3.338057,43.310225]]},"attributes":{"nom_1_gauche":"PL JEAN CHARRON","nom_1_droite":"PL JEAN CHARRON","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":55.6,"duration":0.095,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.338059,43.310229],[3.338062,43.310243],[3.338071,43.310265],[3.338077,43.310306],[3.338068,43.310363],[3.33805,43.310431],[3.338031,43.310506],[3.338012,43.310572]]},"attributes":{"nom_1_gauche":"PL JEAN CHARRON","nom_1_droite":"PL JEAN CHARRON","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":39.2,"duration":0.06666666666666667,"instruction":""},{"geometry":{"type":"LineString","coordinates":[[3.338127,43.310571],[3.339036,43.310531]]},"attributes":{"nom_1_gauche":"R PIERRE AIGRAIN","nom_1_droite":"R PIERRE AIGRAIN","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":83.1,"duration":0.14166666666666666,"instruction":""}]}]} +{"resource":"bdtopo-pgr","resourceVersion":"2022-01-24","start":"2.558203,48.741424","end":"2.559756,48.742116","profile":"car","optimization":"fastest","geometry":{"type":"LineString","coordinates":[[2.558203,48.741424],[2.558191,48.741421],[2.55814,48.741427],[2.558098,48.741441],[2.558069,48.741464],[2.558055,48.741493],[2.558056,48.741526],[2.558067,48.741554],[2.558044,48.741581],[2.558028,48.741616],[2.558028,48.741643],[2.558036,48.741677],[2.558055,48.741702],[2.558085,48.741727],[2.558189,48.741785],[2.558339,48.74187],[2.558424,48.741924],[2.558476,48.74197],[2.558509,48.742012],[2.558536,48.742056],[2.558551,48.742101],[2.558553,48.742145],[2.558554,48.742214],[2.558541,48.742295],[2.558534,48.742352],[2.558534,48.742415],[2.558658,48.742411],[2.558744,48.742411],[2.558853,48.742409],[2.558956,48.742411],[2.559256,48.742421],[2.559396,48.742424],[2.559537,48.742427],[2.559794,48.742423],[2.559913,48.742421],[2.560004,48.742414],[2.560105,48.742407],[2.560166,48.742398],[2.560226,48.742391],[2.560308,48.742377],[2.560395,48.74236],[2.560497,48.742335],[2.56059,48.742309],[2.560763,48.742253],[2.560812,48.742228],[2.560862,48.742203],[2.560952,48.742152],[2.561031,48.742097],[2.561104,48.742034],[2.561167,48.741969],[2.561218,48.741912],[2.561259,48.741852],[2.561289,48.741792],[2.561317,48.741737],[2.561348,48.741648],[2.561373,48.741568],[2.561388,48.741491],[2.561403,48.741414],[2.561412,48.741293],[2.561415,48.74122],[2.561419,48.741112],[2.561381,48.741109],[2.56134,48.741101],[2.561306,48.741087],[2.56128,48.741068],[2.561262,48.741048],[2.561247,48.741027],[2.561239,48.741005],[2.561236,48.740984],[2.561182,48.740983],[2.561054,48.740979],[2.560777,48.740973],[2.560673,48.740993],[2.5606,48.741012],[2.56054,48.741029],[2.56048,48.741057],[2.560425,48.741097],[2.560382,48.741132],[2.560356,48.74117],[2.560337,48.741218],[2.56033,48.741253],[2.560335,48.741304],[2.560344,48.741367],[2.560383,48.741534],[2.56039,48.741581],[2.5604,48.741622],[2.560422,48.74172],[2.56043,48.741776],[2.560431,48.741817],[2.560425,48.741851],[2.56041,48.741889],[2.560388,48.741921],[2.560366,48.741949],[2.560332,48.741969],[2.560306,48.741979],[2.560226,48.741992],[2.560106,48.742004],[2.560045,48.742009],[2.559921,48.742019],[2.559836,48.742029],[2.559793,48.742039],[2.559801,48.742063],[2.559794,48.742088],[2.559776,48.742108],[2.559757,48.742117]]},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.558028,48.740973,2.561419,48.742427],"distance":679.6,"duration":1.2133333333333334,"constraints":[],"portions":[{"start":"2.558203,48.741424","end":"2.559756,48.742116","distance":679.6,"duration":1.2133333333333334,"bbox":[2.558028,48.740973,2.561419,48.742427],"steps":[{"geometry":{"type":"LineString","coordinates":[[2.558203,48.741424],[2.558191,48.741421],[2.55814,48.741427]]},"attributes":{"nom_1_gauche":"R DES AUBEPINES","nom_1_droite":"R DES AUBEPINES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":4.8,"duration":0.01,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.558098,48.741441],[2.558069,48.741464],[2.558055,48.741493],[2.558056,48.741526],[2.558067,48.741554]]},"attributes":{"nom_1_gauche":"R DES AUBEPINES","nom_1_droite":"R DES AUBEPINES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":17,"duration":0.03333333333333333,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.558044,48.741581],[2.558028,48.741616],[2.558028,48.741643],[2.558036,48.741677],[2.558055,48.741702],[2.558085,48.741727],[2.558189,48.741785],[2.558339,48.74187],[2.558424,48.741924],[2.558476,48.74197],[2.558509,48.742012],[2.558536,48.742056],[2.558551,48.742101],[2.558553,48.742145],[2.558554,48.742214],[2.558541,48.742295],[2.558534,48.742352],[2.558534,48.742415]]},"attributes":{"nom_1_gauche":"R DES AUBEPINES","nom_1_droite":"R DES AUBEPINES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":111.3,"duration":0.22333333333333333,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.558658,48.742411],[2.558744,48.742411],[2.558853,48.742409],[2.558956,48.742411],[2.559256,48.742421],[2.559396,48.742424],[2.559537,48.742427]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":73.8,"duration":0.11,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.559794,48.742423],[2.559913,48.742421],[2.560004,48.742414]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":34.5,"duration":0.051666666666666666,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560105,48.742407]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":7.5,"duration":0.011666666666666665,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560166,48.742398],[2.560226,48.742391]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":9.1,"duration":0.013333333333333334,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560308,48.742377]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":6.2,"duration":0.01,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560395,48.74236],[2.560497,48.742335],[2.56059,48.742309],[2.560763,48.742253]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":36.3,"duration":0.055,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560812,48.742228],[2.560862,48.742203],[2.560952,48.742152],[2.561031,48.742097],[2.561104,48.742034],[2.561167,48.741969],[2.561218,48.741912],[2.561259,48.741852],[2.561289,48.741792]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":65.4,"duration":0.09833333333333334,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.561317,48.741737],[2.561348,48.741648],[2.561373,48.741568],[2.561388,48.741491],[2.561403,48.741414],[2.561412,48.741293]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":56.5,"duration":0.08499999999999999,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.561415,48.74122],[2.561419,48.741112]]},"attributes":{"nom_1_gauche":"AV DES UZELLES","nom_1_droite":"AV DES UZELLES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":20.1,"duration":0.030000000000000002,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.561381,48.741109],[2.56134,48.741101],[2.561306,48.741087],[2.56128,48.741068],[2.561262,48.741048],[2.561247,48.741027],[2.561239,48.741005],[2.561236,48.740984]]},"attributes":{"nom_1_gauche":"RPT DE LA BUTTE AU BERGER","nom_1_droite":"RPT DE LA BUTTE AU BERGER","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":21.7,"duration":0.051666666666666666,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.561182,48.740983],[2.561054,48.740979],[2.560777,48.740973]]},"attributes":{"nom_1_gauche":"R DES JACHERES","nom_1_droite":"R DES JACHERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":33.8,"duration":0.06833333333333333,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560673,48.740993],[2.5606,48.741012],[2.56054,48.741029],[2.56048,48.741057]]},"attributes":{"nom_1_gauche":"R DES JACHERES","nom_1_droite":"R DES JACHERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":24,"duration":0.04833333333333333,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.560425,48.741097],[2.560382,48.741132],[2.560356,48.74117],[2.560337,48.741218],[2.56033,48.741253],[2.560335,48.741304],[2.560344,48.741367],[2.560383,48.741534],[2.56039,48.741581]]},"attributes":{"nom_1_gauche":"R DES JACHERES","nom_1_droite":"R DES JACHERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":61.8,"duration":0.12333333333333334,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.5604,48.741622],[2.560422,48.74172],[2.56043,48.741776],[2.560431,48.741817],[2.560425,48.741851],[2.56041,48.741889],[2.560388,48.741921],[2.560366,48.741949],[2.560332,48.741969],[2.560306,48.741979],[2.560226,48.741992],[2.560106,48.742004],[2.560045,48.742009]]},"attributes":{"nom_1_gauche":"R DES JACHERES","nom_1_droite":"R DES JACHERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":67.1,"duration":0.13499999999999998,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.559921,48.742019],[2.559836,48.742029],[2.559793,48.742039]]},"attributes":{"nom_1_gauche":"R DES JACHERES","nom_1_droite":"R DES JACHERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":18.9,"duration":0.03833333333333333,"instruction":{}},{"geometry":{"type":"LineString","coordinates":[[2.559801,48.742063],[2.559794,48.742088],[2.559776,48.742108],[2.559757,48.742117]]},"attributes":{"nom_1_gauche":"R DES JACHERES","nom_1_droite":"R DES JACHERES","cpx_numero":"","cpx_toponyme_route_nommee":""},"distance":9.8,"duration":0.02,"instruction":{}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/features/requestDataTest.feature b/test/functional/request/cucumber/features/requestDataTest.feature index ce5e133..fee32ef 100644 --- a/test/functional/request/cucumber/features/requestDataTest.feature +++ b/test/functional/request/cucumber/features/requestDataTest.feature @@ -36,32 +36,44 @@ Feature: Road2 with data And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "No path found" - Scenario Outline: [] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour (voir ticket #36883) - Given an "" request on operation "route" in api "simple" "1.0.0" + Scenario: [GET] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour (voir ticket #36883) + Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | - | start | 3.818431,49.081335 | - | end | 3.847342,49.083691 | - | intermediates | 3.826778,49.084188\|3.83656,49.083471\|3.844352,49.079101\|3.828451,49.082207 | + | start | 2.557207345962524,48.74254989437114 | + | end | 2.5614130496978755,48.74151692471051 | + | intermediates | 2.558408975601196,48.74189898445621 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the road should be similar to "../../data/2.json" + + Scenario: [POST] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour (voir ticket #36883) + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | start | 2.557207345962524,48.74254989437114 | + | end | 2.5614130496978755,48.74151692471051 | + And with table parameters for "intermediates": + | value | + | 2.558408975601196,48.74189898445621 | When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road And the road should be similar to "../../data/2.json" - Examples: - | method | - | GET | - | POST | Scenario Outline: [] [simple/1.0.0] Route commençant sur une raquette (voir ticket #37674) Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | - | start | 3.3378517,43.310043 | - | end | 3.3390372,43.310542 | + | start | 2.558207,48.741417 | + | end | 2.559752,48.742114 | When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" diff --git a/test/unit/mocha/utils/testsStorageManager.js b/test/unit/mocha/utils/testsStorageManager.js index ba5685b..9db60b0 100644 --- a/test/unit/mocha/utils/testsStorageManager.js +++ b/test/unit/mocha/utils/testsStorageManager.js @@ -12,7 +12,7 @@ describe('Test du storageManager', function() { describe('Test de la fonction checkJsonStorage()', function() { let json = { - "file": "/home/docker/internal/corse-latest.osrm" + "file": "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" }; let jsonNoFile = { From efff1ac90a296dd962e9b29605817c8c10d74c88 Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 1 Apr 2022 14:25:47 +0200 Subject: [PATCH 04/93] test: refacto et ajout de tests sur les donnees --- documentation/apis/simple/1.0.0/api.yaml | 18 -- package.json | 8 +- .../cucumber/data/bduni/common/boucle.json | 1 + .../{1.json => bduni/common/normale.json} | 0 .../cucumber/data/bduni/common/point.json | 1 + .../data/bduni/common/unique-direct.json | 1 + .../data/bduni/common/unique-inverse.json | 1 + .../data/{2.json => bduni/pgr/1.json} | 0 .../data/{3.json => bduni/pgr/2.json} | 0 ...inTest.feature => req-admin-1.0.0.feature} | 0 .../features/req-data-bduni-osrm.feature | 90 +++++++ ...est.feature => req-data-bduni-pgr.feature} | 23 +- .../features/req-simple-1.0.0-common.feature | 52 ++++ ....feature => req-simple-1.0.0-osrm.feature} | 254 ++++++++++++++---- ...t.feature => req-simple-1.0.0-pgr.feature} | 197 -------------- .../cucumber/features/support/world.js | 154 +++++++---- 16 files changed, 461 insertions(+), 339 deletions(-) create mode 100644 test/functional/request/cucumber/data/bduni/common/boucle.json rename test/functional/request/cucumber/data/{1.json => bduni/common/normale.json} (100%) create mode 100644 test/functional/request/cucumber/data/bduni/common/point.json create mode 100644 test/functional/request/cucumber/data/bduni/common/unique-direct.json create mode 100644 test/functional/request/cucumber/data/bduni/common/unique-inverse.json rename test/functional/request/cucumber/data/{2.json => bduni/pgr/1.json} (100%) rename test/functional/request/cucumber/data/{3.json => bduni/pgr/2.json} (100%) rename test/functional/request/cucumber/features/{requestAdminTest.feature => req-admin-1.0.0.feature} (100%) create mode 100644 test/functional/request/cucumber/features/req-data-bduni-osrm.feature rename test/functional/request/cucumber/features/{requestDataTest.feature => req-data-bduni-pgr.feature} (87%) create mode 100644 test/functional/request/cucumber/features/req-simple-1.0.0-common.feature rename test/functional/request/cucumber/features/{requestTest.feature => req-simple-1.0.0-osrm.feature} (81%) rename test/functional/request/cucumber/features/{requestComplementTest.feature => req-simple-1.0.0-pgr.feature} (79%) diff --git a/documentation/apis/simple/1.0.0/api.yaml b/documentation/apis/simple/1.0.0/api.yaml index ba528f4..24fb6fa 100644 --- a/documentation/apis/simple/1.0.0/api.yaml +++ b/documentation/apis/simple/1.0.0/api.yaml @@ -608,12 +608,6 @@ components: format: "float" bbox: type: "string" - departure: - type: "string" - format: "date" - arrival: - type: "string" - format: "date" resource: type: "string" resourceVersion: @@ -652,12 +646,6 @@ components: format: "float" bbox: type: "string" - departure: - type: "string" - format: "date" - arrival: - type: "string" - format: "date" steps: type: "array" items: @@ -745,12 +733,6 @@ components: type: "string" geometry: type: "string" - departure: - type: "string" - format: "date" - arrival: - type: "string" - format: "date" alerts: type: "array" items: diff --git a/package.json b/package.json index 5f52cd9..213b832 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,8 @@ "configCheck": "node ./src/js/road2.js --configCheck", "utest": "mocha --recursive './test/unit/mocha/**/*.js'", "itest": "mocha --recursive './test/integration/mocha/**/*.js'", - "rtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestTest.feature", - "crtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestComplementTest.feature", - "drtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestDataTest.feature", - "artest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/requestAdminTest.feature", - "ctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/configurationTest.feature", - "cctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/configurationComplementTest.feature", + "rtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/req*.feature", + "ctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/configuration*.feature", "lint": "eslint -c eslint.json ./src/", "jsdoc": "jsdoc -c jsdoc.json", "debug": "node --inspect=0.0.0.0:9229 ./src/js/road2.js" diff --git a/test/functional/request/cucumber/data/bduni/common/boucle.json b/test/functional/request/cucumber/data/bduni/common/boucle.json new file mode 100644 index 0000000..e397acc --- /dev/null +++ b/test/functional/request/cucumber/data/bduni/common/boucle.json @@ -0,0 +1 @@ +{"resource":"bduni-idf-osrm","resourceVersion":"2022-01-24","start":"2.276358,48.874805","end":"2.276358,48.874805","profile":"car","optimization":"fastest","geometry":{"coordinates":[[2.276358,48.874805],[2.276375,48.874824],[2.276477,48.874928],[2.27659,48.875039],[2.276766,48.875191],[2.276885,48.875288],[2.277016,48.875386],[2.277332,48.875602],[2.278009,48.876024],[2.278673,48.876429],[2.279096,48.876688],[2.279429,48.876889],[2.279631,48.877032],[2.279784,48.877153],[2.280005,48.877345],[2.280303,48.877632],[2.28042,48.877754],[2.280548,48.877903],[2.280842,48.878287],[2.281187,48.87872],[2.281232,48.87878],[2.281295,48.878874],[2.281402,48.879042],[2.281411,48.879069],[2.281525,48.87929],[2.281605,48.879462],[2.281727,48.879753],[2.281776,48.879879],[2.281825,48.880006],[2.281875,48.880128],[2.281993,48.880409],[2.282105,48.880641],[2.282335,48.88102],[2.28285,48.881737],[2.282976,48.881934],[2.283273,48.882379],[2.283489,48.882622],[2.283756,48.882865],[2.283966,48.883036],[2.284105,48.88314],[2.284243,48.883232],[2.284312,48.883279],[2.284462,48.883378],[2.284622,48.883483],[2.285374,48.884],[2.285497,48.884078],[2.285735,48.884228],[2.286194,48.88449],[2.28621,48.8845],[2.286283,48.884542],[2.286386,48.884607],[2.286483,48.884666],[2.286789,48.884856],[2.288282,48.885779],[2.288482,48.885906],[2.291512,48.887778],[2.291519,48.88778],[2.291821,48.88794],[2.292073,48.888063],[2.29227,48.888148],[2.292586,48.888267],[2.293182,48.888454],[2.293242,48.888471],[2.293391,48.888514],[2.294673,48.88883],[2.294932,48.888897],[2.295799,48.889119],[2.29595,48.889171],[2.296047,48.889204],[2.296524,48.889364],[2.296938,48.889514],[2.297352,48.889671],[2.297596,48.889769],[2.29783,48.889873],[2.298168,48.890025],[2.298538,48.890219],[2.298874,48.890405],[2.299141,48.890552],[2.299423,48.890708],[2.299433,48.890713],[2.299437,48.890715],[2.299474,48.890735],[2.299502,48.890754],[2.29959,48.890815],[2.299904,48.891],[2.300193,48.89118],[2.301175,48.891803],[2.302226,48.892458],[2.302526,48.892636],[2.302728,48.892748],[2.303489,48.893151],[2.303953,48.893422],[2.305199,48.894188],[2.305831,48.894591],[2.30643,48.894945],[2.30654,48.895004],[2.306799,48.895144],[2.30704,48.895261],[2.307251,48.895362],[2.307457,48.895456],[2.307935,48.895648],[2.308562,48.895884],[2.309725,48.896323],[2.31042,48.896584],[2.310511,48.896619],[2.310637,48.896667],[2.310754,48.896711],[2.310921,48.896778],[2.311318,48.896925],[2.312616,48.897413],[2.312695,48.897443],[2.312771,48.897471],[2.313171,48.89762],[2.313288,48.897663],[2.313349,48.897686],[2.313399,48.897704],[2.3135,48.897742],[2.313669,48.897805],[2.314429,48.898085],[2.314628,48.898159],[2.315319,48.898368],[2.31616,48.898622],[2.316335,48.898675],[2.316588,48.898752],[2.317634,48.899068],[2.317956,48.899166],[2.318126,48.899218],[2.318473,48.899322],[2.318788,48.899417],[2.318955,48.899466],[2.319266,48.89956],[2.3195,48.899628],[2.320254,48.899851],[2.321234,48.900141],[2.321749,48.900273],[2.321996,48.900324],[2.322084,48.900339],[2.322218,48.90036],[2.322707,48.90042],[2.323153,48.900456],[2.323487,48.900469],[2.323606,48.900472],[2.324354,48.900488],[2.324701,48.900494],[2.325501,48.90051],[2.326311,48.900529],[2.326809,48.900538],[2.327929,48.900562],[2.328804,48.900578],[2.329538,48.900593],[2.329656,48.900596],[2.329813,48.9006],[2.329992,48.900604],[2.330454,48.900615],[2.331245,48.90063],[2.33256,48.900657],[2.333407,48.900674],[2.333835,48.900682],[2.334262,48.900691],[2.334723,48.900702],[2.334922,48.900705],[2.335799,48.900725],[2.336386,48.900737],[2.338163,48.900773],[2.339749,48.900803],[2.340347,48.900818],[2.340719,48.900825],[2.34081,48.900828],[2.341157,48.900835],[2.342546,48.900864],[2.343506,48.900884],[2.344754,48.900911],[2.345508,48.900926],[2.346254,48.900942],[2.346991,48.900958],[2.347523,48.900969],[2.347727,48.900973],[2.348652,48.900992],[2.349821,48.901013],[2.350997,48.90104],[2.351402,48.90105],[2.351786,48.901058],[2.352035,48.901064],[2.352416,48.901071],[2.352583,48.901072],[2.352698,48.901071],[2.352946,48.901068],[2.353087,48.901066],[2.35326,48.901063],[2.353412,48.901057],[2.353608,48.90105],[2.353776,48.901041],[2.353889,48.901035],[2.354008,48.901027],[2.354161,48.901016],[2.354576,48.900986],[2.354724,48.900976],[2.354844,48.900968],[2.354976,48.90096],[2.355259,48.90094],[2.355439,48.900927],[2.355716,48.900908],[2.355982,48.900887],[2.356238,48.900867],[2.356515,48.900846],[2.356726,48.900831],[2.356926,48.90082],[2.357152,48.900808],[2.35733,48.9008],[2.357585,48.90079],[2.35787,48.900785],[2.358203,48.900781],[2.358491,48.900779],[2.359129,48.900772],[2.359338,48.90077],[2.359878,48.900761],[2.36017,48.900758],[2.361686,48.900743],[2.36241,48.900733],[2.362745,48.900725],[2.36328,48.900722],[2.363907,48.900709],[2.364585,48.900699],[2.364961,48.900673],[2.365082,48.900668],[2.365611,48.900663],[2.365891,48.90066],[2.366295,48.900666],[2.366524,48.900683],[2.367089,48.900677],[2.367712,48.900673],[2.368821,48.900661],[2.370117,48.900646],[2.370341,48.900644],[2.371803,48.900626],[2.372022,48.90062],[2.37222,48.900612],[2.372504,48.900596],[2.372788,48.900575],[2.373259,48.900532],[2.374532,48.90041],[2.375668,48.900301],[2.376187,48.900253],[2.376361,48.900238],[2.376797,48.900202],[2.377193,48.900183],[2.377382,48.900177],[2.377582,48.900171],[2.377861,48.900168],[2.379157,48.900179],[2.379801,48.900187],[2.380034,48.900189],[2.38024,48.90019],[2.380462,48.900194],[2.380891,48.900197],[2.381042,48.900199],[2.381307,48.900201],[2.381457,48.900205],[2.381668,48.900207],[2.381912,48.900212],[2.382279,48.900226],[2.382637,48.900241],[2.383761,48.900301],[2.383927,48.900311],[2.384186,48.900327],[2.384583,48.900347],[2.384814,48.90036],[2.385027,48.900371],[2.385288,48.900386],[2.385556,48.900402],[2.38666,48.900458],[2.387187,48.900471],[2.387938,48.900468],[2.388398,48.900452],[2.388528,48.900445],[2.388801,48.900428],[2.388962,48.900418],[2.389202,48.900397],[2.389463,48.90037],[2.389716,48.900337],[2.389921,48.900308],[2.390182,48.900262],[2.390436,48.900212],[2.390621,48.900165],[2.390836,48.90011],[2.391141,48.900021],[2.391442,48.899912],[2.391658,48.899822],[2.391807,48.899754],[2.391965,48.899673],[2.392127,48.899585],[2.392249,48.899512],[2.392437,48.899392],[2.392867,48.899064],[2.392922,48.899019],[2.393087,48.898862],[2.393197,48.898738],[2.39331,48.898592],[2.393375,48.898506],[2.393435,48.898423],[2.39349,48.89833],[2.393535,48.898253],[2.393587,48.89816],[2.393616,48.898109],[2.393652,48.898038],[2.393797,48.89767],[2.393835,48.897561],[2.393862,48.897478],[2.393897,48.897367],[2.39404,48.896913],[2.394145,48.896571],[2.394271,48.896169],[2.394334,48.895967],[2.394542,48.895303],[2.394614,48.895078],[2.394657,48.894932],[2.394702,48.894743],[2.394876,48.894151],[2.395207,48.892952],[2.39525,48.892796],[2.395314,48.892569],[2.395353,48.892436],[2.39538,48.892328],[2.395445,48.892093],[2.395475,48.891971],[2.395606,48.891515],[2.395666,48.891297],[2.395784,48.890884],[2.395886,48.890523],[2.39593,48.890356],[2.395987,48.890157],[2.396026,48.89002],[2.396216,48.889343],[2.396268,48.88916],[2.39634,48.888901],[2.396407,48.888668],[2.396542,48.888203],[2.396791,48.887311],[2.396957,48.886704],[2.397,48.886577],[2.397035,48.88646],[2.397121,48.886236],[2.397134,48.886201],[2.397165,48.886126],[2.397169,48.886116],[2.397186,48.886076],[2.397211,48.886017],[2.397239,48.885954],[2.397267,48.885893],[2.397361,48.885743],[2.397493,48.88553],[2.397568,48.885416],[2.397572,48.885411],[2.397856,48.885048],[2.3982,48.884679],[2.398452,48.884437],[2.398704,48.884223],[2.398993,48.883996],[2.399132,48.883887],[2.400334,48.88291],[2.400377,48.882876],[2.400437,48.882827],[2.400839,48.88251],[2.401151,48.882264],[2.401876,48.881723],[2.401989,48.881659],[2.402255,48.881514],[2.402391,48.881446],[2.402657,48.881323],[2.402973,48.881197],[2.403259,48.881096],[2.403627,48.880988],[2.404029,48.880881],[2.404386,48.880789],[2.404887,48.880646],[2.405281,48.880534],[2.405579,48.880433],[2.405884,48.880314],[2.406192,48.880188],[2.406488,48.880045],[2.406747,48.879904],[2.406995,48.879734],[2.407217,48.879565],[2.407472,48.879354],[2.407708,48.879135],[2.407833,48.878993],[2.407962,48.878833],[2.408108,48.878628],[2.408613,48.877783],[2.4087,48.877647],[2.408844,48.877415],[2.408998,48.877156],[2.409142,48.876903],[2.409355,48.876522],[2.409575,48.876174],[2.409724,48.875938],[2.409856,48.875708],[2.40992,48.875605],[2.409981,48.875513],[2.410057,48.875393],[2.410192,48.875192],[2.410264,48.875101],[2.410676,48.874603],[2.410922,48.874309],[2.41113,48.874072],[2.41145,48.873713],[2.411788,48.873307],[2.412022,48.873009],[2.412247,48.872718],[2.412431,48.872474],[2.412591,48.872236],[2.412727,48.871998],[2.412855,48.871771],[2.412933,48.871598],[2.413002,48.871424],[2.413068,48.871219],[2.413129,48.871],[2.413177,48.870783],[2.413222,48.870535],[2.413249,48.870277],[2.413255,48.870096],[2.413251,48.869494],[2.413247,48.869154],[2.41324,48.868837],[2.413237,48.868553],[2.413235,48.868322],[2.413217,48.867349],[2.413216,48.867179],[2.413204,48.866639],[2.413196,48.866153],[2.413192,48.865816],[2.41319,48.865646],[2.413191,48.865416],[2.413201,48.865129],[2.413212,48.864882],[2.413248,48.864475],[2.413275,48.864257],[2.413349,48.863769],[2.413394,48.863481],[2.413453,48.863109],[2.413527,48.862657],[2.413541,48.862566],[2.413757,48.86123],[2.413806,48.860938],[2.413825,48.86082],[2.413901,48.860344],[2.414005,48.859689],[2.414098,48.85891],[2.414107,48.858463],[2.414102,48.858357],[2.414058,48.857807],[2.413942,48.856712],[2.413917,48.856465],[2.413894,48.856234],[2.413728,48.856099],[2.413657,48.855853],[2.413637,48.855785],[2.413554,48.855404],[2.413465,48.854779],[2.413454,48.854686],[2.413414,48.85457],[2.41332,48.854534],[2.413227,48.854486],[2.413125,48.854421],[2.413056,48.85436],[2.412996,48.854266],[2.412966,48.854148],[2.412968,48.85403],[2.413002,48.853911],[2.413045,48.853831],[2.413104,48.853755],[2.413196,48.853678],[2.413295,48.853625],[2.413431,48.853572],[2.413581,48.853535],[2.413659,48.853518],[2.413773,48.853505],[2.413891,48.853503],[2.413938,48.853508],[2.414068,48.853528],[2.414193,48.853557],[2.414301,48.853592],[2.414387,48.853627],[2.414463,48.85367],[2.414515,48.853715],[2.414562,48.853769],[2.414607,48.85383],[2.414664,48.853934],[2.41468,48.854045],[2.414644,48.854219],[2.414827,48.854252],[2.415051,48.854292],[2.415301,48.854333],[2.415303,48.854181],[2.415316,48.854132],[2.41537,48.853941],[2.41544,48.853559],[2.415461,48.853438],[2.41549,48.853284],[2.415512,48.853136],[2.415546,48.852928],[2.415576,48.85273],[2.415611,48.852522],[2.415654,48.852303],[2.41569,48.852111],[2.415738,48.851857],[2.415786,48.851571],[2.415843,48.851245],[2.415892,48.85099],[2.416038,48.850121],[2.416138,48.84958],[2.41615,48.849515],[2.416193,48.849311],[2.416205,48.849264],[2.416341,48.849234],[2.416456,48.849239],[2.416695,48.849247],[2.417537,48.849284],[2.417644,48.849309],[2.417666,48.849106],[2.417672,48.848878],[2.417658,48.848204],[2.417638,48.847609],[2.417621,48.847135],[2.417613,48.846795],[2.417548,48.846708],[2.417515,48.846656],[2.417488,48.846611],[2.417463,48.846558],[2.41703,48.846601],[2.416487,48.846657],[2.415984,48.846695],[2.415876,48.846703],[2.415579,48.846718],[2.415407,48.846732],[2.415317,48.846748],[2.415191,48.846788],[2.415173,48.846822],[2.41515,48.846855],[2.415119,48.846886],[2.415085,48.846912],[2.415035,48.846945],[2.414955,48.846986],[2.414886,48.84701],[2.414871,48.847016],[2.414766,48.847041],[2.414698,48.847054],[2.414647,48.847065],[2.414509,48.847082],[2.414466,48.847084],[2.41437,48.847086],[2.414313,48.847084],[2.414222,48.847079],[2.414135,48.847055],[2.414068,48.847033],[2.41401,48.847002],[2.413932,48.846954],[2.413875,48.846892],[2.413846,48.846844],[2.413831,48.846808],[2.413826,48.846767],[2.413828,48.846703],[2.413837,48.846678],[2.413852,48.846649],[2.413878,48.84662],[2.413899,48.846597],[2.413927,48.846505],[2.413948,48.846432],[2.413946,48.846359],[2.413944,48.846212],[2.413913,48.845791],[2.413883,48.845437],[2.413872,48.845339],[2.413818,48.844854],[2.413805,48.844646],[2.413823,48.844497],[2.413888,48.844319],[2.413772,48.843733],[2.413728,48.843378],[2.413689,48.842958],[2.413644,48.842399],[2.413591,48.841949],[2.413538,48.841324],[2.41353,48.841221],[2.413518,48.841103],[2.413485,48.840716],[2.413397,48.840249],[2.41327,48.839717],[2.413155,48.839322],[2.412712,48.837848],[2.412689,48.837778],[2.412558,48.837402],[2.412405,48.836955],[2.412346,48.836759],[2.412264,48.836524],[2.412154,48.83626],[2.412062,48.836069],[2.412054,48.836052],[2.411979,48.835919],[2.411862,48.835716],[2.41159,48.835392],[2.411407,48.835215],[2.411195,48.835031],[2.410971,48.834865],[2.410738,48.834707],[2.410336,48.834484],[2.407818,48.833547],[2.407161,48.833284],[2.406761,48.833093],[2.406444,48.832921],[2.405585,48.832424],[2.403557,48.831274],[2.403116,48.831011],[2.402789,48.830802],[2.402264,48.830454],[2.401597,48.829992],[2.400881,48.829523],[2.400482,48.829315],[2.399929,48.829095],[2.399509,48.828973],[2.399047,48.828858],[2.398973,48.82884],[2.398666,48.828772],[2.398606,48.828758],[2.398496,48.828734],[2.398392,48.828711],[2.398269,48.828682],[2.398156,48.828657],[2.398041,48.828631],[2.397993,48.828619],[2.397905,48.8286],[2.397842,48.828585],[2.397735,48.828561],[2.397504,48.828508],[2.397318,48.828466],[2.397151,48.828428],[2.396988,48.828391],[2.396819,48.828353],[2.396732,48.828333],[2.396641,48.828311],[2.396561,48.828293],[2.396471,48.828273],[2.396329,48.828241],[2.396209,48.828212],[2.396088,48.828184],[2.395923,48.828148],[2.395849,48.82813],[2.395762,48.82811],[2.395601,48.828074],[2.395537,48.828061],[2.395345,48.828022],[2.395302,48.828014],[2.395032,48.827967],[2.394724,48.827922],[2.394633,48.827911],[2.394395,48.827883],[2.393827,48.827816],[2.393325,48.827757],[2.393191,48.82774],[2.393029,48.82772],[2.392921,48.827708],[2.392795,48.827692],[2.3927,48.827682],[2.392585,48.827668],[2.392466,48.827652],[2.392338,48.827629],[2.392233,48.827612],[2.392119,48.827592],[2.391964,48.827564],[2.391794,48.827524],[2.391619,48.827481],[2.391425,48.827431],[2.391199,48.827371],[2.390958,48.827298],[2.390753,48.827229],[2.390586,48.827173],[2.390467,48.827131],[2.390334,48.827086],[2.390206,48.827042],[2.390051,48.826988],[2.38997,48.826962],[2.389928,48.826947],[2.389878,48.826932],[2.389802,48.82691],[2.389662,48.826867],[2.389409,48.82679],[2.388766,48.826572],[2.388407,48.82645],[2.38826,48.826396],[2.38814,48.826353],[2.387619,48.826164],[2.387259,48.82604],[2.386911,48.825914],[2.386329,48.825715],[2.385977,48.825599],[2.385652,48.825478],[2.38553,48.825433],[2.385187,48.825288],[2.384936,48.82517],[2.384745,48.825084],[2.384645,48.825035],[2.384079,48.824744],[2.383898,48.824636],[2.383593,48.824457],[2.383238,48.824247],[2.383006,48.824107],[2.38285,48.824013],[2.382547,48.82383],[2.382407,48.823747],[2.382191,48.823617],[2.381742,48.823348],[2.38144,48.82317],[2.381124,48.822979],[2.380803,48.822788],[2.3806,48.822666],[2.380366,48.822526],[2.380128,48.822383],[2.379803,48.822189],[2.37952,48.82202],[2.379316,48.821897],[2.379161,48.821806],[2.378941,48.821681],[2.378784,48.821602],[2.378689,48.821554],[2.378394,48.821418],[2.378134,48.821315],[2.377899,48.821227],[2.377479,48.821071],[2.376993,48.820912],[2.376639,48.820796],[2.376082,48.820622],[2.37545,48.820418],[2.3744,48.820074],[2.373528,48.819784],[2.37279,48.819542],[2.372131,48.819325],[2.371375,48.819076],[2.371196,48.819017],[2.370556,48.818809],[2.370503,48.818792],[2.370092,48.818658],[2.370065,48.81865],[2.36967,48.818522],[2.369162,48.818354],[2.368644,48.818185],[2.368019,48.817978],[2.367392,48.81777],[2.366903,48.817604],[2.366375,48.817427],[2.365957,48.817272],[2.3657,48.817166],[2.365391,48.817036],[2.365033,48.816878],[2.364681,48.816717],[2.364464,48.81662],[2.364347,48.816567],[2.364073,48.816453],[2.363893,48.816379],[2.363716,48.816318],[2.363476,48.816259],[2.363295,48.816219],[2.363097,48.816187],[2.362909,48.816159],[2.362752,48.816144],[2.362455,48.816121],[2.361998,48.81611],[2.36135,48.816142],[2.360513,48.816197],[2.359615,48.816261],[2.358972,48.816306],[2.358403,48.81634],[2.357982,48.816359],[2.357687,48.816379],[2.357531,48.81639],[2.357128,48.816433],[2.35689,48.816466],[2.356625,48.816524],[2.356465,48.816563],[2.356278,48.816626],[2.356171,48.816662],[2.356061,48.816702],[2.355917,48.816754],[2.355694,48.816862],[2.355498,48.816961],[2.355481,48.816969],[2.355347,48.817047],[2.355122,48.81717],[2.354727,48.817401],[2.354294,48.817621],[2.354051,48.817728],[2.354038,48.817733],[2.353661,48.817836],[2.353324,48.817919],[2.353166,48.81795],[2.353042,48.817965],[2.352841,48.817993],[2.352653,48.818013],[2.352447,48.818023],[2.352133,48.818024],[2.351862,48.818016],[2.351557,48.817988],[2.351254,48.817962],[2.350977,48.817915],[2.350701,48.817857],[2.350401,48.817787],[2.350146,48.817718],[2.349869,48.817636],[2.349633,48.817554],[2.349307,48.81744],[2.348986,48.81731],[2.348672,48.817181],[2.348511,48.817108],[2.348438,48.817073],[2.348383,48.817047],[2.348214,48.816969],[2.347954,48.816866],[2.347816,48.816811],[2.347703,48.816765],[2.347503,48.816691],[2.347341,48.816638],[2.346982,48.816537],[2.346709,48.81648],[2.346404,48.816438],[2.346227,48.816424],[2.345991,48.816495],[2.345548,48.816523],[2.345225,48.816546],[2.344809,48.816566],[2.344701,48.816579],[2.34461,48.816597],[2.344525,48.816624],[2.344424,48.816671],[2.344311,48.816745],[2.344223,48.816805],[2.344177,48.816819],[2.344124,48.816822],[2.344076,48.816818],[2.344024,48.81681],[2.343978,48.816795],[2.343973,48.816685],[2.343968,48.816605],[2.343956,48.816306],[2.343946,48.81621],[2.343941,48.816158],[2.34393,48.816104],[2.343919,48.816048],[2.343909,48.815999],[2.343919,48.81595],[2.343929,48.815925],[2.343866,48.815897],[2.343811,48.815882],[2.343775,48.815877],[2.343724,48.815872],[2.343685,48.815875],[2.343646,48.815879],[2.343582,48.815891],[2.34343,48.815944],[2.343296,48.815992],[2.343157,48.816045],[2.343013,48.816085],[2.342877,48.816112],[2.342187,48.81624],[2.341801,48.816308],[2.341658,48.816322],[2.34103,48.81636],[2.340512,48.816372],[2.34032,48.816386],[2.339543,48.816436],[2.338645,48.816485],[2.338007,48.816515],[2.33751,48.816546],[2.337275,48.816558],[2.336674,48.816592],[2.336332,48.816611],[2.336103,48.816624],[2.335581,48.816655],[2.335146,48.816682],[2.334935,48.816694],[2.333711,48.816771],[2.332145,48.816982],[2.331996,48.817001],[2.331898,48.817011],[2.331773,48.817026],[2.331642,48.817038],[2.331433,48.817061],[2.330791,48.817132],[2.330155,48.817182],[2.329947,48.817192],[2.328821,48.817283],[2.328496,48.817306],[2.328341,48.817309],[2.32818,48.817308],[2.327906,48.817285],[2.326756,48.817399],[2.325667,48.817501],[2.325647,48.817662],[2.325654,48.817827],[2.325665,48.818221],[2.325682,48.818822],[2.325709,48.819386],[2.325739,48.819612],[2.325748,48.81972],[2.325759,48.819798],[2.326214,48.819708],[2.326682,48.819571],[2.326787,48.819543],[2.327213,48.819445],[2.328492,48.819159],[2.328617,48.819131],[2.329311,48.818978],[2.329301,48.81895],[2.329165,48.818481],[2.329109,48.818308],[2.328855,48.817409],[2.328821,48.817283],[2.328496,48.817306],[2.328341,48.817309],[2.32818,48.817308],[2.327906,48.817285],[2.326756,48.817399],[2.325667,48.817501],[2.325647,48.817662],[2.325654,48.817827],[2.325665,48.818221],[2.325682,48.818822],[2.325709,48.819386],[2.325739,48.819612],[2.325748,48.81972],[2.325759,48.819798],[2.325764,48.819897],[2.325732,48.820159],[2.325739,48.820252],[2.325606,48.820298],[2.3255,48.820341],[2.325425,48.820376],[2.324625,48.820558],[2.324213,48.820559],[2.324063,48.820583],[2.32304,48.820803],[2.322535,48.820898],[2.322348,48.820929],[2.322132,48.82095],[2.321882,48.820961],[2.321592,48.820955],[2.320486,48.821176],[2.319356,48.821429],[2.318717,48.821568],[2.316763,48.822014],[2.316574,48.82207],[2.315929,48.822258],[2.315623,48.822338],[2.31524,48.822423],[2.31409,48.822684],[2.312227,48.823099],[2.311364,48.823291],[2.310272,48.823537],[2.308081,48.82402],[2.307599,48.824127],[2.304842,48.824742],[2.304372,48.82485],[2.301721,48.82546],[2.29952,48.825923],[2.298956,48.826049],[2.298438,48.826164],[2.296875,48.826518],[2.296547,48.826588],[2.29558,48.8268],[2.294579,48.82703],[2.29399,48.827159],[2.293614,48.827237],[2.293121,48.827358],[2.292475,48.827532],[2.291797,48.827741],[2.291232,48.827941],[2.290752,48.82813],[2.290645,48.828178],[2.290401,48.828282],[2.290015,48.828459],[2.288973,48.828931],[2.288394,48.829182],[2.287685,48.8295],[2.286752,48.82992],[2.285798,48.830338],[2.2849,48.830715],[2.284032,48.831075],[2.283048,48.831473],[2.281608,48.832068],[2.280687,48.83244],[2.279812,48.832798],[2.279223,48.833043],[2.278582,48.833298],[2.277536,48.833724],[2.277457,48.833757],[2.277128,48.833891],[2.276843,48.834007],[2.276681,48.834072],[2.276542,48.834129],[2.275811,48.834425],[2.275496,48.83454],[2.275331,48.834599],[2.275272,48.834617],[2.27505,48.834687],[2.2748,48.834761],[2.274514,48.834842],[2.274295,48.834899],[2.274187,48.834922],[2.273996,48.834968],[2.273706,48.835035],[2.273385,48.835103],[2.273089,48.835165],[2.272665,48.835244],[2.272454,48.835278],[2.272378,48.835288],[2.272108,48.835325],[2.271826,48.835358],[2.271582,48.835377],[2.271344,48.835388],[2.271042,48.8354],[2.270637,48.835402],[2.270374,48.835395],[2.270174,48.835385],[2.269966,48.83537],[2.269826,48.835358],[2.269615,48.835333],[2.269463,48.835314],[2.267892,48.835081],[2.267539,48.835027],[2.267302,48.834994],[2.267132,48.834968],[2.266962,48.834949],[2.266742,48.834931],[2.266399,48.83491],[2.26614,48.8349],[2.265899,48.834897],[2.265662,48.834901],[2.265332,48.834916],[2.26505,48.834933],[2.263967,48.835],[2.261195,48.835157],[2.261139,48.83516],[2.260977,48.83517],[2.260546,48.835197],[2.260157,48.835237],[2.259959,48.835263],[2.259621,48.835317],[2.259388,48.835359],[2.259162,48.8354],[2.259006,48.83544],[2.258741,48.835511],[2.258422,48.835603],[2.258148,48.835692],[2.257899,48.835778],[2.257597,48.835902],[2.257381,48.836],[2.257223,48.836072],[2.257164,48.836104],[2.257147,48.836112],[2.257098,48.836138],[2.256946,48.83622],[2.256786,48.836305],[2.256344,48.836546],[2.25586,48.836875],[2.255626,48.837061],[2.255502,48.837168],[2.255381,48.837279],[2.255282,48.837373],[2.255164,48.837497],[2.255012,48.837671],[2.254929,48.83777],[2.254865,48.837849],[2.254766,48.837986],[2.254594,48.838234],[2.25451,48.838377],[2.254439,48.838524],[2.254359,48.838736],[2.254322,48.838856],[2.254243,48.839141],[2.254202,48.839347],[2.254162,48.839587],[2.254111,48.839893],[2.254095,48.840042],[2.254089,48.840367],[2.254115,48.840634],[2.254196,48.841089],[2.254294,48.841654],[2.254443,48.8425],[2.25453,48.843011],[2.254601,48.843273],[2.254705,48.843652],[2.254996,48.844689],[2.255041,48.844872],[2.25508,48.845097],[2.255125,48.845517],[2.255114,48.84572],[2.255103,48.845806],[2.255069,48.845987],[2.255034,48.846152],[2.254996,48.846301],[2.254945,48.84645],[2.254832,48.846722],[2.254666,48.847012],[2.254617,48.847081],[2.254558,48.847164],[2.254444,48.847316],[2.254277,48.847502],[2.254119,48.847653],[2.253895,48.847863],[2.25354,48.848177],[2.253125,48.848603],[2.252935,48.848846],[2.252677,48.849234],[2.252516,48.849538],[2.252419,48.849746],[2.252351,48.849947],[2.252296,48.850136],[2.252231,48.850441],[2.252214,48.850682],[2.252211,48.850967],[2.252228,48.85119],[2.252289,48.851621],[2.252328,48.851812],[2.252354,48.85193],[2.252375,48.851991],[2.252426,48.852106],[2.252471,48.852203],[2.252583,48.852397],[2.252649,48.852511],[2.252801,48.852748],[2.252894,48.852877],[2.25299,48.853004],[2.253253,48.853276],[2.253326,48.853344],[2.253627,48.853608],[2.25387,48.853809],[2.253975,48.853887],[2.254075,48.853962],[2.254201,48.85406],[2.254243,48.854088],[2.254865,48.854511],[2.255578,48.855011],[2.256824,48.855854],[2.2575,48.856298],[2.258365,48.856901],[2.259231,48.857461],[2.259504,48.857616],[2.259818,48.857782],[2.260034,48.857893],[2.260244,48.857992],[2.260644,48.858164],[2.260979,48.858306],[2.261367,48.858477],[2.261783,48.858677],[2.262025,48.858806],[2.262214,48.85892],[2.262414,48.859051],[2.262565,48.85916],[2.262741,48.859297],[2.262986,48.859513],[2.263149,48.859687],[2.263252,48.859787],[2.263362,48.859906],[2.263441,48.86001],[2.2635,48.860088],[2.263638,48.860301],[2.263694,48.860395],[2.263757,48.860494],[2.263835,48.860633],[2.26396,48.86085],[2.264194,48.861267],[2.264305,48.861452],[2.264676,48.862075],[2.264804,48.862286],[2.265001,48.862561],[2.265203,48.862816],[2.265376,48.863016],[2.265784,48.863383],[2.266052,48.863609],[2.2666,48.864023],[2.267077,48.864383],[2.267251,48.864546],[2.267521,48.864809],[2.267675,48.864973],[2.267874,48.865197],[2.268028,48.865385],[2.268175,48.86558],[2.268249,48.865681],[2.268334,48.865807],[2.268498,48.866059],[2.268689,48.866351],[2.268811,48.866539],[2.268949,48.866755],[2.269079,48.866955],[2.269253,48.867206],[2.269453,48.867482],[2.26965,48.867732],[2.269844,48.867992],[2.270022,48.868211],[2.270565,48.868765],[2.271253,48.869398],[2.271342,48.869471],[2.271532,48.86964],[2.271985,48.870027],[2.272335,48.87032],[2.272646,48.870586],[2.272953,48.870882],[2.273484,48.871388],[2.273666,48.871563],[2.273833,48.871737],[2.273989,48.871937],[2.274135,48.872154],[2.274314,48.872405],[2.274552,48.872716],[2.27488,48.873106],[2.275289,48.87358],[2.275799,48.874167],[2.276083,48.874497],[2.276239,48.874676],[2.276358,48.874805]],"type":"LineString"},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.252211,48.815872,2.417672,48.901072],"distance":36965.5,"duration":25.944999999999997,"constraints":[],"portions":[{"start":"2.276358,48.874805","end":"2.416138,48.84958","distance":16046.7,"duration":10.25,"bbox":[2.276358,48.84958,2.416138,48.901072],"steps":[{"geometry":{"coordinates":[[2.276358,48.874805],[2.276375,48.874824],[2.276477,48.874928],[2.27659,48.875039],[2.276766,48.875191],[2.276885,48.875288],[2.277016,48.875386],[2.277332,48.875602],[2.278009,48.876024],[2.278673,48.876429],[2.279096,48.876688],[2.279429,48.876889],[2.279631,48.877032],[2.279784,48.877153],[2.280005,48.877345],[2.280303,48.877632],[2.28042,48.877754],[2.280548,48.877903],[2.280842,48.878287],[2.281187,48.87872],[2.281232,48.87878],[2.281295,48.878874],[2.281402,48.879042],[2.281411,48.879069],[2.281525,48.87929],[2.281605,48.879462],[2.281727,48.879753],[2.281776,48.879879],[2.281825,48.880006],[2.281875,48.880128],[2.281993,48.880409],[2.282105,48.880641],[2.282335,48.88102],[2.28285,48.881737],[2.282976,48.881934],[2.283273,48.882379],[2.283489,48.882622],[2.283756,48.882865],[2.283966,48.883036],[2.284105,48.88314],[2.284243,48.883232],[2.284312,48.883279],[2.284462,48.883378],[2.284622,48.883483],[2.285374,48.884],[2.285497,48.884078],[2.285735,48.884228],[2.286194,48.88449],[2.28621,48.8845],[2.286283,48.884542],[2.286386,48.884607],[2.286483,48.884666],[2.286789,48.884856],[2.288282,48.885779],[2.288482,48.885906],[2.291512,48.887778],[2.291519,48.88778],[2.291821,48.88794],[2.292073,48.888063],[2.29227,48.888148],[2.292586,48.888267],[2.293182,48.888454],[2.293242,48.888471],[2.293391,48.888514],[2.294673,48.88883],[2.294932,48.888897],[2.295799,48.889119],[2.29595,48.889171],[2.296047,48.889204],[2.296524,48.889364],[2.296938,48.889514],[2.297352,48.889671],[2.297596,48.889769],[2.29783,48.889873],[2.298168,48.890025],[2.298538,48.890219],[2.298874,48.890405],[2.299141,48.890552],[2.299423,48.890708],[2.299433,48.890713],[2.299437,48.890715],[2.299474,48.890735],[2.299502,48.890754],[2.29959,48.890815],[2.299904,48.891],[2.300193,48.89118],[2.301175,48.891803],[2.302226,48.892458],[2.302526,48.892636],[2.302728,48.892748],[2.303489,48.893151],[2.303953,48.893422],[2.305199,48.894188],[2.305831,48.894591],[2.30643,48.894945],[2.30654,48.895004],[2.306799,48.895144],[2.30704,48.895261],[2.307251,48.895362],[2.307457,48.895456],[2.307935,48.895648],[2.308562,48.895884],[2.309725,48.896323],[2.31042,48.896584],[2.310511,48.896619],[2.310637,48.896667],[2.310754,48.896711],[2.310921,48.896778],[2.311318,48.896925],[2.312616,48.897413],[2.312695,48.897443],[2.312771,48.897471],[2.313171,48.89762],[2.313288,48.897663],[2.313349,48.897686],[2.313399,48.897704],[2.3135,48.897742],[2.313669,48.897805],[2.314429,48.898085],[2.314628,48.898159],[2.315319,48.898368],[2.31616,48.898622],[2.316335,48.898675],[2.316588,48.898752],[2.317634,48.899068],[2.317956,48.899166],[2.318126,48.899218],[2.318473,48.899322],[2.318788,48.899417],[2.318955,48.899466],[2.319266,48.89956],[2.3195,48.899628],[2.320254,48.899851],[2.321234,48.900141],[2.321749,48.900273],[2.321996,48.900324],[2.322084,48.900339],[2.322218,48.90036],[2.322707,48.90042],[2.323153,48.900456],[2.323487,48.900469],[2.323606,48.900472],[2.324354,48.900488],[2.324701,48.900494],[2.325501,48.90051],[2.326311,48.900529],[2.326809,48.900538],[2.327929,48.900562],[2.328804,48.900578],[2.329538,48.900593],[2.329656,48.900596],[2.329813,48.9006],[2.329992,48.900604],[2.330454,48.900615],[2.331245,48.90063],[2.33256,48.900657],[2.333407,48.900674],[2.333835,48.900682],[2.334262,48.900691],[2.334723,48.900702],[2.334922,48.900705],[2.335799,48.900725],[2.336386,48.900737],[2.338163,48.900773],[2.339749,48.900803],[2.340347,48.900818],[2.340719,48.900825],[2.34081,48.900828],[2.341157,48.900835],[2.342546,48.900864],[2.343506,48.900884],[2.344754,48.900911],[2.345508,48.900926],[2.346254,48.900942],[2.346991,48.900958],[2.347523,48.900969],[2.347727,48.900973],[2.348652,48.900992],[2.349821,48.901013],[2.350997,48.90104],[2.351402,48.90105],[2.351786,48.901058],[2.352035,48.901064],[2.352416,48.901071],[2.352583,48.901072],[2.352698,48.901071],[2.352946,48.901068],[2.353087,48.901066],[2.35326,48.901063],[2.353412,48.901057],[2.353608,48.90105],[2.353776,48.901041],[2.353889,48.901035],[2.354008,48.901027],[2.354161,48.901016],[2.354576,48.900986],[2.354724,48.900976],[2.354844,48.900968],[2.354976,48.90096],[2.355259,48.90094],[2.355439,48.900927],[2.355716,48.900908],[2.355982,48.900887],[2.356238,48.900867],[2.356515,48.900846],[2.356726,48.900831],[2.356926,48.90082],[2.357152,48.900808],[2.35733,48.9008],[2.357585,48.90079],[2.35787,48.900785],[2.358203,48.900781],[2.358491,48.900779],[2.359129,48.900772],[2.359338,48.90077],[2.359878,48.900761],[2.36017,48.900758],[2.361686,48.900743],[2.36241,48.900733],[2.362745,48.900725],[2.36328,48.900722],[2.363907,48.900709],[2.364585,48.900699],[2.364961,48.900673],[2.365082,48.900668],[2.365611,48.900663],[2.365891,48.90066],[2.366295,48.900666],[2.366524,48.900683],[2.367089,48.900677],[2.367712,48.900673],[2.368821,48.900661],[2.370117,48.900646],[2.370341,48.900644],[2.371803,48.900626],[2.372022,48.90062],[2.37222,48.900612],[2.372504,48.900596],[2.372788,48.900575],[2.373259,48.900532],[2.374532,48.90041],[2.375668,48.900301],[2.376187,48.900253],[2.376361,48.900238],[2.376797,48.900202],[2.377193,48.900183],[2.377382,48.900177],[2.377582,48.900171],[2.377861,48.900168],[2.379157,48.900179],[2.379801,48.900187],[2.380034,48.900189],[2.38024,48.90019],[2.380462,48.900194],[2.380891,48.900197],[2.381042,48.900199],[2.381307,48.900201],[2.381457,48.900205],[2.381668,48.900207],[2.381912,48.900212],[2.382279,48.900226],[2.382637,48.900241],[2.383761,48.900301],[2.383927,48.900311],[2.384186,48.900327],[2.384583,48.900347],[2.384814,48.90036],[2.385027,48.900371],[2.385288,48.900386],[2.385556,48.900402],[2.38666,48.900458],[2.387187,48.900471],[2.387938,48.900468],[2.388398,48.900452],[2.388528,48.900445],[2.388801,48.900428],[2.388962,48.900418],[2.389202,48.900397],[2.389463,48.90037],[2.389716,48.900337],[2.389921,48.900308],[2.390182,48.900262],[2.390436,48.900212],[2.390621,48.900165],[2.390836,48.90011],[2.391141,48.900021],[2.391442,48.899912],[2.391658,48.899822],[2.391807,48.899754],[2.391965,48.899673],[2.392127,48.899585],[2.392249,48.899512],[2.392437,48.899392],[2.392867,48.899064],[2.392922,48.899019],[2.393087,48.898862],[2.393197,48.898738],[2.39331,48.898592],[2.393375,48.898506],[2.393435,48.898423],[2.39349,48.89833],[2.393535,48.898253],[2.393587,48.89816],[2.393616,48.898109],[2.393652,48.898038],[2.393797,48.89767],[2.393835,48.897561],[2.393862,48.897478],[2.393897,48.897367],[2.39404,48.896913],[2.394145,48.896571],[2.394271,48.896169],[2.394334,48.895967],[2.394542,48.895303],[2.394614,48.895078],[2.394657,48.894932],[2.394702,48.894743],[2.394876,48.894151],[2.395207,48.892952],[2.39525,48.892796],[2.395314,48.892569],[2.395353,48.892436],[2.39538,48.892328],[2.395445,48.892093],[2.395475,48.891971],[2.395606,48.891515],[2.395666,48.891297],[2.395784,48.890884],[2.395886,48.890523],[2.39593,48.890356],[2.395987,48.890157],[2.396026,48.89002],[2.396216,48.889343],[2.396268,48.88916],[2.39634,48.888901],[2.396407,48.888668],[2.396542,48.888203],[2.396791,48.887311],[2.396957,48.886704],[2.397,48.886577],[2.397035,48.88646],[2.397121,48.886236],[2.397134,48.886201],[2.397165,48.886126],[2.397169,48.886116],[2.397186,48.886076],[2.397211,48.886017],[2.397239,48.885954],[2.397267,48.885893],[2.397361,48.885743],[2.397493,48.88553],[2.397568,48.885416],[2.397572,48.885411],[2.397856,48.885048],[2.3982,48.884679],[2.398452,48.884437],[2.398704,48.884223],[2.398993,48.883996],[2.399132,48.883887],[2.400334,48.88291],[2.400377,48.882876],[2.400437,48.882827],[2.400839,48.88251]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":11712.7,"duration":7.111666666666666,"instruction":{"type":"depart","modifier":"right"}},{"geometry":{"coordinates":[[2.400839,48.88251],[2.401151,48.882264],[2.401876,48.881723],[2.401989,48.881659],[2.402255,48.881514],[2.402391,48.881446],[2.402657,48.881323],[2.402973,48.881197],[2.403259,48.881096],[2.403627,48.880988],[2.404029,48.880881],[2.404386,48.880789],[2.404887,48.880646],[2.405281,48.880534],[2.405579,48.880433],[2.405884,48.880314],[2.406192,48.880188],[2.406488,48.880045],[2.406747,48.879904],[2.406995,48.879734],[2.407217,48.879565],[2.407472,48.879354],[2.407708,48.879135],[2.407833,48.878993],[2.407962,48.878833],[2.408108,48.878628],[2.408613,48.877783],[2.4087,48.877647],[2.408844,48.877415],[2.408998,48.877156],[2.409142,48.876903],[2.409355,48.876522],[2.409575,48.876174],[2.409724,48.875938],[2.409856,48.875708],[2.40992,48.875605],[2.409981,48.875513],[2.410057,48.875393],[2.410192,48.875192],[2.410264,48.875101],[2.410676,48.874603],[2.410922,48.874309],[2.41113,48.874072],[2.41145,48.873713],[2.411788,48.873307],[2.412022,48.873009],[2.412247,48.872718],[2.412431,48.872474],[2.412591,48.872236],[2.412727,48.871998],[2.412855,48.871771],[2.412933,48.871598],[2.413002,48.871424],[2.413068,48.871219],[2.413129,48.871],[2.413177,48.870783],[2.413222,48.870535],[2.413249,48.870277],[2.413255,48.870096],[2.413251,48.869494],[2.413247,48.869154],[2.41324,48.868837],[2.413237,48.868553],[2.413235,48.868322],[2.413217,48.867349],[2.413216,48.867179],[2.413204,48.866639],[2.413196,48.866153],[2.413192,48.865816],[2.41319,48.865646],[2.413191,48.865416],[2.413201,48.865129],[2.413212,48.864882],[2.413248,48.864475],[2.413275,48.864257],[2.413349,48.863769],[2.413394,48.863481],[2.413453,48.863109],[2.413527,48.862657],[2.413541,48.862566],[2.413757,48.86123],[2.413806,48.860938],[2.413825,48.86082],[2.413901,48.860344],[2.414005,48.859689],[2.414098,48.85891],[2.414107,48.858463],[2.414102,48.858357],[2.414058,48.857807],[2.413942,48.856712],[2.413917,48.856465],[2.413894,48.856234]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":3277,"duration":1.995,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.413894,48.856234],[2.413728,48.856099],[2.413657,48.855853],[2.413637,48.855785],[2.413554,48.855404],[2.413465,48.854779],[2.413454,48.854686],[2.413414,48.85457]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":""}},"distance":191.1,"duration":0.2533333333333333,"instruction":{"type":"turn","modifier":"slight right"}},{"geometry":{"coordinates":[[2.413414,48.85457],[2.41332,48.854534],[2.413227,48.854486],[2.413125,48.854421],[2.413056,48.85436],[2.412996,48.854266],[2.412966,48.854148],[2.412968,48.85403],[2.413002,48.853911],[2.413045,48.853831],[2.413104,48.853755],[2.413196,48.853678],[2.413295,48.853625],[2.413431,48.853572],[2.413581,48.853535],[2.413659,48.853518],[2.413773,48.853505],[2.413891,48.853503],[2.413938,48.853508],[2.414068,48.853528],[2.414193,48.853557],[2.414301,48.853592],[2.414387,48.853627],[2.414463,48.85367],[2.414515,48.853715],[2.414562,48.853769],[2.414607,48.85383],[2.414664,48.853934],[2.41468,48.854045],[2.414644,48.854219]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL DE LA PORTE DE MONTREUIL","nom_1_droite":"PL DE LA PORTE DE MONTREUIL","cpx_numero":"","cpx_toponyme":""}},"distance":283.8,"duration":0.3383333333333333,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.414644,48.854219],[2.414827,48.854252],[2.415051,48.854292],[2.415301,48.854333]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL DE LA PORTE DE MONTREUIL","nom_1_droite":"PL DE LA PORTE DE MONTREUIL","cpx_numero":"","cpx_toponyme":""}},"distance":49.7,"duration":0.04833333333333333,"instruction":{"type":"continue","modifier":"right"}},{"geometry":{"coordinates":[[2.415301,48.854333],[2.415303,48.854181],[2.415316,48.854132],[2.41537,48.853941],[2.41544,48.853559],[2.415461,48.853438],[2.41549,48.853284],[2.415512,48.853136],[2.415546,48.852928],[2.415576,48.85273],[2.415611,48.852522],[2.415654,48.852303],[2.41569,48.852111]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV BENOIT FRACHON","nom_1_droite":"AV BENOIT FRACHON","cpx_numero":"","cpx_toponyme":""}},"distance":248.9,"duration":0.235,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.41569,48.852111],[2.415738,48.851857],[2.415786,48.851571],[2.415843,48.851245],[2.415892,48.85099],[2.416038,48.850121],[2.416138,48.84958]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV LEON GAUMONT","nom_1_droite":"AV LEON GAUMONT","cpx_numero":"","cpx_toponyme":""}},"distance":283.4,"duration":0.26833333333333337,"instruction":{"type":"new name","modifier":"straight"}},{"geometry":{"coordinates":[[2.416138,48.84958],[2.416138,48.84958]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV LEON GAUMONT","nom_1_droite":"AV LEON GAUMONT","cpx_numero":"","cpx_toponyme":""}},"distance":0,"duration":0,"instruction":{"type":"arrive"}}]},{"start":"2.416138,48.84958","end":"2.328617,48.819131","distance":9680.3,"duration":8.101666666666667,"bbox":[2.325647,48.815872,2.417672,48.84958],"steps":[{"geometry":{"coordinates":[[2.416138,48.84958],[2.41615,48.849515],[2.416193,48.849311],[2.416205,48.849264]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV LEON GAUMONT","nom_1_droite":"AV LEON GAUMONT","cpx_numero":"","cpx_toponyme":""}},"distance":35.5,"duration":0.03666666666666667,"instruction":{"type":"depart"}},{"geometry":{"coordinates":[[2.416205,48.849264],[2.416341,48.849234],[2.416456,48.849239],[2.416695,48.849247],[2.417537,48.849284],[2.417644,48.849309]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE LAGNY","nom_1_droite":"R DE LAGNY","cpx_numero":"D143A","cpx_toponyme":""}},"distance":106.5,"duration":0.14166666666666666,"instruction":{"type":"turn","modifier":"left"}},{"geometry":{"coordinates":[[2.417644,48.849309],[2.417666,48.849106],[2.417672,48.848878],[2.417658,48.848204],[2.417638,48.847609],[2.417621,48.847135],[2.417613,48.846795],[2.417548,48.846708],[2.417515,48.846656],[2.417488,48.846611],[2.417463,48.846558]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV JOFFRE","nom_1_droite":"AV JOFFRE","cpx_numero":"D158","cpx_toponyme":""}},"distance":308.3,"duration":0.41000000000000003,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.417463,48.846558],[2.41703,48.846601],[2.416487,48.846657],[2.415984,48.846695],[2.415876,48.846703],[2.415579,48.846718],[2.415407,48.846732],[2.415317,48.846748],[2.415191,48.846788],[2.415173,48.846822],[2.41515,48.846855],[2.415119,48.846886],[2.415085,48.846912]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV GALLIENI","nom_1_droite":"AV GALLIENI","cpx_numero":"D120","cpx_toponyme":""}},"distance":184.8,"duration":0.24333333333333335,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.415085,48.846912],[2.415035,48.846945],[2.414955,48.846986],[2.414886,48.84701],[2.414871,48.847016],[2.414766,48.847041],[2.414698,48.847054],[2.414647,48.847065],[2.414509,48.847082],[2.414466,48.847084],[2.41437,48.847086],[2.414313,48.847084],[2.414222,48.847079],[2.414135,48.847055],[2.414068,48.847033],[2.41401,48.847002],[2.413932,48.846954],[2.413875,48.846892]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV DE LA PORTE DE VINCENNES","nom_1_droite":"AV DE LA PORTE DE VINCENNES","cpx_numero":"","cpx_toponyme":""}},"distance":101.9,"duration":0.12333333333333334,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.413875,48.846892],[2.413846,48.846844],[2.413831,48.846808],[2.413826,48.846767],[2.413828,48.846703],[2.413837,48.846678],[2.413852,48.846649],[2.413878,48.84662],[2.413899,48.846597]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV DE LA PORTE DE VINCENNES","nom_1_droite":"AV DE LA PORTE DE VINCENNES","cpx_numero":"","cpx_toponyme":""}},"distance":34.6,"duration":0.04,"instruction":{"type":"continue","modifier":"left"}},{"geometry":{"coordinates":[[2.413899,48.846597],[2.413927,48.846505],[2.413948,48.846432],[2.413946,48.846359],[2.413944,48.846212],[2.413913,48.845791],[2.413883,48.845437],[2.413872,48.845339],[2.413818,48.844854],[2.413805,48.844646],[2.413823,48.844497],[2.413888,48.844319]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":""}},"distance":254.6,"duration":0.33999999999999997,"instruction":{"type":"turn","modifier":"straight"}},{"geometry":{"coordinates":[[2.413888,48.844319],[2.413772,48.843733],[2.413728,48.843378],[2.413689,48.842958],[2.413644,48.842399],[2.413591,48.841949],[2.413538,48.841324],[2.41353,48.841221],[2.413518,48.841103],[2.413485,48.840716],[2.413397,48.840249],[2.41327,48.839717],[2.413155,48.839322],[2.412712,48.837848],[2.412689,48.837778],[2.412558,48.837402],[2.412405,48.836955],[2.412346,48.836759],[2.412264,48.836524],[2.412154,48.83626],[2.412062,48.836069],[2.412054,48.836052],[2.411979,48.835919],[2.411862,48.835716],[2.41159,48.835392],[2.411407,48.835215],[2.411195,48.835031],[2.410971,48.834865],[2.410738,48.834707],[2.410336,48.834484],[2.407818,48.833547],[2.407161,48.833284],[2.406761,48.833093],[2.406444,48.832921],[2.405585,48.832424],[2.403557,48.831274],[2.403116,48.831011],[2.402789,48.830802],[2.402264,48.830454],[2.401597,48.829992],[2.400881,48.829523],[2.400482,48.829315],[2.399929,48.829095],[2.399509,48.828973],[2.399047,48.828858],[2.398973,48.82884],[2.398666,48.828772],[2.398606,48.828758],[2.398496,48.828734],[2.398392,48.828711],[2.398269,48.828682],[2.398156,48.828657],[2.398041,48.828631],[2.397993,48.828619],[2.397905,48.8286],[2.397842,48.828585],[2.397735,48.828561],[2.397504,48.828508],[2.397318,48.828466],[2.397151,48.828428],[2.396988,48.828391],[2.396819,48.828353],[2.396732,48.828333],[2.396641,48.828311],[2.396561,48.828293],[2.396471,48.828273],[2.396329,48.828241],[2.396209,48.828212],[2.396088,48.828184],[2.395923,48.828148],[2.395849,48.82813],[2.395762,48.82811],[2.395601,48.828074],[2.395537,48.828061],[2.395345,48.828022],[2.395302,48.828014],[2.395032,48.827967],[2.394724,48.827922]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":2528.1,"duration":1.52,"instruction":{"type":"turn","modifier":"straight"}},{"geometry":{"coordinates":[[2.394724,48.827922],[2.394633,48.827911],[2.394395,48.827883],[2.393827,48.827816],[2.393325,48.827757],[2.393191,48.82774],[2.393029,48.82772],[2.392921,48.827708],[2.392795,48.827692],[2.3927,48.827682],[2.392585,48.827668],[2.392466,48.827652],[2.392338,48.827629],[2.392233,48.827612],[2.392119,48.827592],[2.391964,48.827564],[2.391794,48.827524],[2.391619,48.827481],[2.391425,48.827431],[2.391199,48.827371],[2.390958,48.827298],[2.390753,48.827229],[2.390586,48.827173],[2.390467,48.827131],[2.390334,48.827086],[2.390206,48.827042],[2.390051,48.826988],[2.38997,48.826962],[2.389928,48.826947],[2.389878,48.826932],[2.389802,48.82691],[2.389662,48.826867],[2.389409,48.82679],[2.388766,48.826572],[2.388407,48.82645],[2.38826,48.826396],[2.38814,48.826353],[2.387619,48.826164],[2.387259,48.82604],[2.386911,48.825914],[2.386329,48.825715],[2.385977,48.825599],[2.385652,48.825478],[2.38553,48.825433],[2.385187,48.825288],[2.384936,48.82517],[2.384745,48.825084],[2.384645,48.825035],[2.384079,48.824744],[2.383898,48.824636],[2.383593,48.824457],[2.383238,48.824247],[2.383006,48.824107],[2.38285,48.824013],[2.382547,48.82383],[2.382407,48.823747],[2.382191,48.823617],[2.381742,48.823348],[2.38144,48.82317],[2.381124,48.822979],[2.380803,48.822788],[2.3806,48.822666],[2.380366,48.822526],[2.380128,48.822383],[2.379803,48.822189],[2.37952,48.82202],[2.379316,48.821897],[2.379161,48.821806],[2.378941,48.821681],[2.378784,48.821602],[2.378689,48.821554],[2.378394,48.821418],[2.378134,48.821315],[2.377899,48.821227],[2.377479,48.821071],[2.376993,48.820912],[2.376639,48.820796],[2.376082,48.820622],[2.37545,48.820418],[2.3744,48.820074],[2.373528,48.819784],[2.37279,48.819542],[2.372131,48.819325],[2.371375,48.819076],[2.371196,48.819017],[2.370556,48.818809],[2.370503,48.818792],[2.370092,48.818658],[2.370065,48.81865],[2.36967,48.818522],[2.369162,48.818354],[2.368644,48.818185],[2.368019,48.817978],[2.367392,48.81777],[2.366903,48.817604],[2.366375,48.817427]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":2408.2,"duration":1.4633333333333336,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.366375,48.817427],[2.365957,48.817272],[2.3657,48.817166],[2.365391,48.817036],[2.365033,48.816878],[2.364681,48.816717],[2.364464,48.81662]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":166.4,"duration":0.10333333333333333,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.364464,48.81662],[2.364347,48.816567],[2.364073,48.816453],[2.363893,48.816379],[2.363716,48.816318],[2.363476,48.816259],[2.363295,48.816219],[2.363097,48.816187],[2.362909,48.816159],[2.362752,48.816144],[2.362455,48.816121],[2.361998,48.81611],[2.36135,48.816142],[2.360513,48.816197],[2.359615,48.816261],[2.358972,48.816306],[2.358403,48.81634],[2.357982,48.816359],[2.357687,48.816379],[2.357531,48.81639],[2.357128,48.816433],[2.35689,48.816466],[2.356625,48.816524],[2.356465,48.816563],[2.356278,48.816626],[2.356171,48.816662],[2.356061,48.816702],[2.355917,48.816754],[2.355694,48.816862],[2.355498,48.816961],[2.355481,48.816969],[2.355347,48.817047],[2.355122,48.81717],[2.354727,48.817401],[2.354294,48.817621],[2.354051,48.817728],[2.354038,48.817733],[2.353661,48.817836],[2.353324,48.817919],[2.353166,48.81795],[2.353042,48.817965],[2.352841,48.817993],[2.352653,48.818013],[2.352447,48.818023],[2.352133,48.818024],[2.351862,48.818016],[2.351557,48.817988],[2.351254,48.817962],[2.350977,48.817915],[2.350701,48.817857],[2.350401,48.817787],[2.350146,48.817718],[2.349869,48.817636],[2.349633,48.817554],[2.349307,48.81744],[2.348986,48.81731],[2.348672,48.817181],[2.348511,48.817108],[2.348438,48.817073],[2.348383,48.817047],[2.348214,48.816969],[2.347954,48.816866],[2.347816,48.816811],[2.347703,48.816765],[2.347503,48.816691],[2.347341,48.816638],[2.346982,48.816537],[2.346709,48.81648],[2.346404,48.816438],[2.346227,48.816424]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":1442.5,"duration":0.9116666666666665,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.346227,48.816424],[2.345991,48.816495],[2.345548,48.816523],[2.345225,48.816546],[2.344809,48.816566],[2.344701,48.816579],[2.34461,48.816597],[2.344525,48.816624],[2.344424,48.816671],[2.344311,48.816745],[2.344223,48.816805],[2.344177,48.816819],[2.344124,48.816822],[2.344076,48.816818],[2.344024,48.81681],[2.343978,48.816795]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":""}},"distance":176.7,"duration":0.23333333333333334,"instruction":{"type":"turn","modifier":"slight right"}},{"geometry":{"coordinates":[[2.343978,48.816795],[2.343973,48.816685],[2.343968,48.816605],[2.343956,48.816306],[2.343946,48.81621],[2.343941,48.816158],[2.34393,48.816104],[2.343919,48.816048],[2.343909,48.815999],[2.343919,48.81595],[2.343929,48.815925]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV PIERRE DE COUBERTIN","nom_1_droite":"AV PIERRE DE COUBERTIN","cpx_numero":"","cpx_toponyme":""}},"distance":97.1,"duration":0.14,"instruction":{"type":"end of road","modifier":"left"}},{"geometry":{"coordinates":[[2.343929,48.815925],[2.343866,48.815897],[2.343811,48.815882],[2.343775,48.815877],[2.343724,48.815872],[2.343685,48.815875],[2.343646,48.815879],[2.343582,48.815891],[2.34343,48.815944],[2.343296,48.815992],[2.343157,48.816045],[2.343013,48.816085],[2.342877,48.816112],[2.342187,48.81624],[2.341801,48.816308],[2.341658,48.816322],[2.34103,48.81636],[2.340512,48.816372],[2.34032,48.816386],[2.339543,48.816436],[2.338645,48.816485],[2.338007,48.816515],[2.33751,48.816546],[2.337275,48.816558],[2.336674,48.816592],[2.336332,48.816611],[2.336103,48.816624],[2.335581,48.816655],[2.335146,48.816682],[2.334935,48.816694],[2.333711,48.816771],[2.332145,48.816982],[2.331996,48.817001],[2.331898,48.817011]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV PAUL VAILLANT-COUTURIER","nom_1_droite":"AV PAUL VAILLANT-COUTURIER","cpx_numero":"D150","cpx_toponyme":""}},"distance":896.5,"duration":1.1883333333333335,"instruction":{"type":"end of road","modifier":"right"}},{"geometry":{"coordinates":[[2.331898,48.817011],[2.331773,48.817026],[2.331642,48.817038],[2.331433,48.817061],[2.330791,48.817132],[2.330155,48.817182],[2.329947,48.817192],[2.328821,48.817283],[2.328496,48.817306],[2.328341,48.817309],[2.32818,48.817308],[2.327906,48.817285],[2.326756,48.817399],[2.325667,48.817501]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R BARBES","nom_1_droite":"R BARBES","cpx_numero":"D50","cpx_toponyme":""}},"distance":460.5,"duration":0.61,"instruction":{"type":"new name","modifier":"straight"}},{"geometry":{"coordinates":[[2.325667,48.817501],[2.325647,48.817662],[2.325654,48.817827],[2.325665,48.818221],[2.325682,48.818822],[2.325709,48.819386],[2.325739,48.819612],[2.325748,48.81972],[2.325759,48.819798]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV ARISTIDE BRIAND","nom_1_droite":"AV ARISTIDE BRIAND","cpx_numero":"D920","cpx_toponyme":""}},"distance":255.8,"duration":0.30666666666666664,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.325759,48.819798],[2.326214,48.819708]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"BD ROMAIN ROLLAND","nom_1_droite":"BD ROMAIN ROLLAND","cpx_numero":"","cpx_toponyme":""}},"distance":34.8,"duration":0.041666666666666664,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.326214,48.819708],[2.326682,48.819571],[2.326787,48.819543],[2.327213,48.819445],[2.328492,48.819159],[2.328617,48.819131]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"BD ROMAIN ROLLAND","nom_1_droite":"BD ROMAIN ROLLAND","cpx_numero":"","cpx_toponyme":""}},"distance":187.4,"duration":0.24833333333333332,"instruction":{"type":"fork","modifier":"slight right"}},{"geometry":{"coordinates":[[2.328617,48.819131],[2.328617,48.819131]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"BD ROMAIN ROLLAND","nom_1_droite":"BD ROMAIN ROLLAND","cpx_numero":"","cpx_toponyme":""}},"distance":0,"duration":0,"instruction":{"type":"arrive","modifier":"right"}}]},{"start":"2.328617,48.819131","end":"2.276358,48.874805","distance":11238.5,"duration":7.593333333333334,"bbox":[2.276358,48.815872,2.417672,48.901072],"steps":[{"geometry":{"coordinates":[[2.328617,48.819131],[2.329311,48.818978]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"BD ROMAIN ROLLAND","nom_1_droite":"BD ROMAIN ROLLAND","cpx_numero":"","cpx_toponyme":""}},"distance":53.6,"duration":0.07333333333333333,"instruction":{"type":"depart","modifier":"right"}},{"geometry":{"coordinates":[[2.329311,48.818978],[2.329301,48.81895],[2.329165,48.818481],[2.329109,48.818308],[2.328855,48.817409],[2.328821,48.817283]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R FRANCOIS ORY","nom_1_droite":"R FRANCOIS ORY","cpx_numero":"","cpx_toponyme":""}},"distance":191.9,"duration":0.385,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.328821,48.817283],[2.328496,48.817306],[2.328341,48.817309],[2.32818,48.817308],[2.327906,48.817285],[2.326756,48.817399],[2.325667,48.817501]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R BARBES","nom_1_droite":"R BARBES","cpx_numero":"D50","cpx_toponyme":""}},"distance":233.1,"duration":0.30833333333333335,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.325667,48.817501],[2.325647,48.817662],[2.325654,48.817827],[2.325665,48.818221],[2.325682,48.818822],[2.325709,48.819386],[2.325739,48.819612],[2.325748,48.81972],[2.325759,48.819798],[2.325764,48.819897],[2.325732,48.820159],[2.325739,48.820252]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"AV ARISTIDE BRIAND","nom_1_droite":"AV ARISTIDE BRIAND","cpx_numero":"D920","cpx_toponyme":""}},"distance":306.4,"duration":0.36666666666666664,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.325739,48.820252],[2.325606,48.820298],[2.3255,48.820341],[2.325425,48.820376],[2.324625,48.820558],[2.324213,48.820559],[2.324063,48.820583],[2.32304,48.820803],[2.322535,48.820898],[2.322348,48.820929],[2.322132,48.82095],[2.321882,48.820961],[2.321592,48.820955]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":""}},"distance":317.3,"duration":0.41000000000000003,"instruction":{"type":"turn","modifier":"left"}},{"geometry":{"coordinates":[[2.321592,48.820955],[2.320486,48.821176],[2.319356,48.821429],[2.318717,48.821568],[2.316763,48.822014],[2.316574,48.82207],[2.315929,48.822258],[2.315623,48.822338],[2.31524,48.822423],[2.31409,48.822684],[2.312227,48.823099],[2.311364,48.823291],[2.310272,48.823537],[2.308081,48.82402],[2.307599,48.824127],[2.304842,48.824742],[2.304372,48.82485]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":1333.7,"duration":0.8433333333333334,"instruction":{"type":"turn","modifier":"straight"}},{"geometry":{"coordinates":[[2.304372,48.82485],[2.301721,48.82546],[2.29952,48.825923],[2.298956,48.826049],[2.298438,48.826164],[2.296875,48.826518],[2.296547,48.826588],[2.29558,48.8268],[2.294579,48.82703],[2.29399,48.827159],[2.293614,48.827237],[2.293121,48.827358],[2.292475,48.827532],[2.291797,48.827741],[2.291232,48.827941],[2.290752,48.82813],[2.290645,48.828178],[2.290401,48.828282],[2.290015,48.828459],[2.288973,48.828931],[2.288394,48.829182],[2.287685,48.8295],[2.286752,48.82992],[2.285798,48.830338],[2.2849,48.830715],[2.284032,48.831075],[2.283048,48.831473],[2.281608,48.832068],[2.280687,48.83244],[2.279812,48.832798],[2.279223,48.833043],[2.278582,48.833298],[2.277536,48.833724],[2.277457,48.833757],[2.277128,48.833891],[2.276843,48.834007],[2.276681,48.834072],[2.276542,48.834129],[2.275811,48.834425],[2.275496,48.83454],[2.275331,48.834599],[2.275272,48.834617],[2.27505,48.834687],[2.2748,48.834761],[2.274514,48.834842],[2.274295,48.834899],[2.274187,48.834922],[2.273996,48.834968],[2.273706,48.835035],[2.273385,48.835103],[2.273089,48.835165],[2.272665,48.835244],[2.272454,48.835278],[2.272378,48.835288]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":2632.8,"duration":1.5716666666666663,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.272378,48.835288],[2.272108,48.835325],[2.271826,48.835358],[2.271582,48.835377],[2.271344,48.835388],[2.271042,48.8354],[2.270637,48.835402],[2.270374,48.835395],[2.270174,48.835385],[2.269966,48.83537],[2.269826,48.835358],[2.269615,48.835333],[2.269463,48.835314],[2.267892,48.835081],[2.267539,48.835027],[2.267302,48.834994],[2.267132,48.834968],[2.266962,48.834949],[2.266742,48.834931],[2.266399,48.83491],[2.26614,48.8349],[2.265899,48.834897],[2.265662,48.834901],[2.265332,48.834916],[2.26505,48.834933],[2.263967,48.835],[2.261195,48.835157],[2.261139,48.83516],[2.260977,48.83517]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":842.4,"duration":0.495,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.260977,48.83517],[2.260546,48.835197],[2.260157,48.835237],[2.259959,48.835263],[2.259621,48.835317],[2.259388,48.835359],[2.259162,48.8354],[2.259006,48.83544],[2.258741,48.835511],[2.258422,48.835603],[2.258148,48.835692],[2.257899,48.835778],[2.257597,48.835902],[2.257381,48.836],[2.257223,48.836072],[2.257164,48.836104],[2.257147,48.836112],[2.257098,48.836138],[2.256946,48.83622],[2.256786,48.836305],[2.256344,48.836546],[2.25586,48.836875],[2.255626,48.837061],[2.255502,48.837168],[2.255381,48.837279],[2.255282,48.837373],[2.255164,48.837497],[2.255012,48.837671],[2.254929,48.83777],[2.254865,48.837849],[2.254766,48.837986],[2.254594,48.838234],[2.25451,48.838377],[2.254439,48.838524],[2.254359,48.838736],[2.254322,48.838856],[2.254243,48.839141],[2.254202,48.839347],[2.254162,48.839587],[2.254111,48.839893],[2.254095,48.840042],[2.254089,48.840367],[2.254115,48.840634],[2.254196,48.841089],[2.254294,48.841654],[2.254443,48.8425],[2.25453,48.843011],[2.254601,48.843273]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":1175.6,"duration":0.7283333333333334,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.254601,48.843273],[2.254705,48.843652],[2.254996,48.844689],[2.255041,48.844872],[2.25508,48.845097],[2.255125,48.845517],[2.255114,48.84572],[2.255103,48.845806],[2.255069,48.845987]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":305.2,"duration":0.19499999999999998,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.255069,48.845987],[2.255034,48.846152],[2.254996,48.846301],[2.254945,48.84645],[2.254832,48.846722],[2.254666,48.847012],[2.254617,48.847081],[2.254558,48.847164],[2.254444,48.847316],[2.254277,48.847502],[2.254119,48.847653],[2.253895,48.847863],[2.25354,48.848177],[2.253125,48.848603],[2.252935,48.848846],[2.252677,48.849234],[2.252516,48.849538],[2.252419,48.849746],[2.252351,48.849947],[2.252296,48.850136],[2.252231,48.850441],[2.252214,48.850682],[2.252211,48.850967],[2.252228,48.85119],[2.252289,48.851621],[2.252328,48.851812],[2.252354,48.85193],[2.252375,48.851991],[2.252426,48.852106],[2.252471,48.852203],[2.252583,48.852397],[2.252649,48.852511],[2.252801,48.852748],[2.252894,48.852877],[2.25299,48.853004],[2.253253,48.853276],[2.253326,48.853344],[2.253627,48.853608],[2.25387,48.853809],[2.253975,48.853887],[2.254075,48.853962],[2.254201,48.85406],[2.254243,48.854088],[2.254865,48.854511],[2.255578,48.855011],[2.256824,48.855854],[2.2575,48.856298],[2.258365,48.856901],[2.259231,48.857461],[2.259504,48.857616],[2.259818,48.857782],[2.260034,48.857893],[2.260244,48.857992],[2.260644,48.858164],[2.260979,48.858306],[2.261367,48.858477],[2.261783,48.858677],[2.262025,48.858806],[2.262214,48.85892],[2.262414,48.859051],[2.262565,48.85916],[2.262741,48.859297],[2.262986,48.859513],[2.263149,48.859687],[2.263252,48.859787],[2.263362,48.859906],[2.263441,48.86001],[2.2635,48.860088],[2.263638,48.860301],[2.263694,48.860395],[2.263757,48.860494],[2.263835,48.860633],[2.26396,48.86085],[2.264194,48.861267],[2.264305,48.861452],[2.264676,48.862075],[2.264804,48.862286],[2.265001,48.862561],[2.265203,48.862816],[2.265376,48.863016],[2.265784,48.863383],[2.266052,48.863609],[2.2666,48.864023],[2.267077,48.864383],[2.267251,48.864546],[2.267521,48.864809],[2.267675,48.864973],[2.267874,48.865197],[2.268028,48.865385],[2.268175,48.86558],[2.268249,48.865681],[2.268334,48.865807],[2.268498,48.866059],[2.268689,48.866351],[2.268811,48.866539],[2.268949,48.866755],[2.269079,48.866955],[2.269253,48.867206],[2.269453,48.867482],[2.26965,48.867732],[2.269844,48.867992],[2.270022,48.868211],[2.270565,48.868765],[2.271253,48.869398],[2.271342,48.869471],[2.271532,48.86964],[2.271985,48.870027],[2.272335,48.87032],[2.272646,48.870586],[2.272953,48.870882],[2.273484,48.871388],[2.273666,48.871563],[2.273833,48.871737],[2.273989,48.871937],[2.274135,48.872154],[2.274314,48.872405],[2.274552,48.872716],[2.27488,48.873106],[2.275289,48.87358],[2.275799,48.874167],[2.276083,48.874497],[2.276239,48.874676],[2.276358,48.874805]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":3846.5,"duration":2.216666666666667,"instruction":{"type":"fork","modifier":"slight left"}},{"geometry":{"coordinates":[[2.276358,48.874805],[2.276358,48.874805]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":0,"duration":0,"instruction":{"type":"arrive","modifier":"right"}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/data/1.json b/test/functional/request/cucumber/data/bduni/common/normale.json similarity index 100% rename from test/functional/request/cucumber/data/1.json rename to test/functional/request/cucumber/data/bduni/common/normale.json diff --git a/test/functional/request/cucumber/data/bduni/common/point.json b/test/functional/request/cucumber/data/bduni/common/point.json new file mode 100644 index 0000000..f7c6c7b --- /dev/null +++ b/test/functional/request/cucumber/data/bduni/common/point.json @@ -0,0 +1 @@ +{"resource":"bdtopo-osrm","resourceVersion":"2022-01-24","start":"2.276358,48.874805","end":"2.276358,48.874805","profile":"car","optimization":"fastest","geometry":{"coordinates":[[2.276358,48.874805],[2.276358,48.874805]],"type":"LineString"},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.276358,48.874805,2.276358,48.874805],"distance":0,"duration":0,"constraints":[],"portions":[{"start":"2.276358,48.874805","end":"2.276358,48.874805","distance":0,"duration":0,"bbox":[2.276358,48.874805,2.276358,48.874805],"steps":[{"geometry":{"coordinates":[[2.276358,48.874805],[2.276358,48.874805]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":0,"duration":0,"instruction":{"type":"depart","modifier":"straight"}},{"geometry":{"coordinates":[[2.276358,48.874805],[2.276358,48.874805]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"","nom_1_droite":"","cpx_numero":"","cpx_toponyme":"Boulevard Périphérique Intérieur"}},"distance":0,"duration":0,"instruction":{"type":"arrive","modifier":"straight"}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/data/bduni/common/unique-direct.json b/test/functional/request/cucumber/data/bduni/common/unique-direct.json new file mode 100644 index 0000000..55e34ba --- /dev/null +++ b/test/functional/request/cucumber/data/bduni/common/unique-direct.json @@ -0,0 +1 @@ +{"resource":"bdtopo-osrm","resourceVersion":"2022-01-24","start":"2.326638,48.875382","end":"2.326728,48.874912","profile":"car","optimization":"fastest","geometry":{"coordinates":[[2.326638,48.875382],[2.326637,48.875355],[2.326663,48.875223],[2.326707,48.875012],[2.326728,48.874912]],"type":"LineString"},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.326637,48.874912,2.326728,48.875382],"distance":52.7,"duration":0.07833333333333334,"constraints":[],"portions":[{"start":"2.326638,48.875382","end":"2.326728,48.874912","distance":52.7,"duration":0.07833333333333334,"bbox":[2.326637,48.874912,2.326728,48.875382],"steps":[{"geometry":{"coordinates":[[2.326638,48.875382],[2.326637,48.875355],[2.326663,48.875223],[2.326707,48.875012],[2.326728,48.874912]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DU HAVRE","nom_1_droite":"R DU HAVRE","cpx_numero":"","cpx_toponyme":""}},"distance":52.7,"duration":0.07833333333333334,"instruction":{"type":"depart"}},{"geometry":{"coordinates":[[2.326728,48.874912],[2.326728,48.874912]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DU HAVRE","nom_1_droite":"R DU HAVRE","cpx_numero":"","cpx_toponyme":""}},"distance":0,"duration":0,"instruction":{"type":"arrive"}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/data/bduni/common/unique-inverse.json b/test/functional/request/cucumber/data/bduni/common/unique-inverse.json new file mode 100644 index 0000000..ddd9e30 --- /dev/null +++ b/test/functional/request/cucumber/data/bduni/common/unique-inverse.json @@ -0,0 +1 @@ +{"resource":"bdtopo-osrm","resourceVersion":"2022-01-24","start":"2.326877,48.874383","end":"2.326638,48.875371","profile":"car","optimization":"fastest","geometry":{"coordinates":[[2.326877,48.874383],[2.326937,48.874179],[2.325756,48.874164],[2.324868,48.874785],[2.324727,48.874875],[2.324579,48.874965],[2.324501,48.875021],[2.324217,48.875252],[2.324436,48.875289],[2.32484,48.875341],[2.32499,48.875355],[2.325089,48.875369],[2.326288,48.875547],[2.3264,48.875558],[2.326506,48.875562],[2.326666,48.875544],[2.32664,48.875446],[2.326638,48.875371]],"type":"LineString"},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.324217,48.874164,2.326937,48.875562],"distance":477.3,"duration":0.6883333333333332,"constraints":[],"portions":[{"start":"2.326877,48.874383","end":"2.326638,48.875371","distance":477.3,"duration":0.6883333333333332,"bbox":[2.324217,48.874164,2.326937,48.875562],"steps":[{"geometry":{"coordinates":[[2.326877,48.874383],[2.326937,48.874179]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DU HAVRE","nom_1_droite":"R DU HAVRE","cpx_numero":"","cpx_toponyme":""}},"distance":23.1,"duration":0.035,"instruction":{"type":"depart"}},{"geometry":{"coordinates":[[2.326937,48.874179],[2.325756,48.874164]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE PROVENCE","nom_1_droite":"R DE PROVENCE","cpx_numero":"","cpx_toponyme":""}},"distance":86.4,"duration":0.13,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.325756,48.874164],[2.324868,48.874785],[2.324727,48.874875],[2.324579,48.874965],[2.324501,48.875021],[2.324217,48.875252]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE ROME","nom_1_droite":"R DE ROME","cpx_numero":"","cpx_toponyme":""}},"distance":165.4,"duration":0.22,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.324217,48.875252],[2.324436,48.875289],[2.32484,48.875341]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL GABRIEL PERI","nom_1_droite":"PL GABRIEL PERI","cpx_numero":"","cpx_toponyme":""}},"distance":46.7,"duration":0.07,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.32484,48.875341],[2.32499,48.875355],[2.325089,48.875369],[2.326288,48.875547],[2.3264,48.875558],[2.326506,48.875562],[2.326666,48.875544]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R SAINT-LAZARE","nom_1_droite":"R SAINT-LAZARE","cpx_numero":"","cpx_toponyme":""}},"distance":136.3,"duration":0.20500000000000002,"instruction":{"type":"new name","modifier":"straight"}},{"geometry":{"coordinates":[[2.326666,48.875544],[2.32664,48.875446],[2.326638,48.875371]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DU HAVRE","nom_1_droite":"R DU HAVRE","cpx_numero":"","cpx_toponyme":""}},"distance":19.4,"duration":0.028333333333333332,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.326638,48.875371],[2.326638,48.875371]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DU HAVRE","nom_1_droite":"R DU HAVRE","cpx_numero":"","cpx_toponyme":""}},"distance":0,"duration":0,"instruction":{"type":"arrive"}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/data/2.json b/test/functional/request/cucumber/data/bduni/pgr/1.json similarity index 100% rename from test/functional/request/cucumber/data/2.json rename to test/functional/request/cucumber/data/bduni/pgr/1.json diff --git a/test/functional/request/cucumber/data/3.json b/test/functional/request/cucumber/data/bduni/pgr/2.json similarity index 100% rename from test/functional/request/cucumber/data/3.json rename to test/functional/request/cucumber/data/bduni/pgr/2.json diff --git a/test/functional/request/cucumber/features/requestAdminTest.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature similarity index 100% rename from test/functional/request/cucumber/features/requestAdminTest.feature rename to test/functional/request/cucumber/features/req-admin-1.0.0.feature diff --git a/test/functional/request/cucumber/features/req-data-bduni-osrm.feature b/test/functional/request/cucumber/features/req-data-bduni-osrm.feature new file mode 100644 index 0000000..f43685e --- /dev/null +++ b/test/functional/request/cucumber/features/req-data-bduni-osrm.feature @@ -0,0 +1,90 @@ +Feature: Road2 with Bduni data via OSRM + Tests fonctionnels de Road2 prenant en compte la donnée Bduni via le moteur OSRM + + Background: + Given I have loaded all my test configuration in "../../configurations/local.json" + + Scenario Outline: [] [simple/1.0.0] Route normale + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | start | 2.3420083522796626,48.845718486262086 | + | end | 2.3216879367828365,48.84558433605804 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the road should be similar to "../../data/bduni/common/normale.json" + + Examples: + | method | + | GET | + | POST | + + Scenario: [GET] [simple/1.0.0] Route qui fait une boucle + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | start | 2.276744842529297,48.874651226434366 | + | end | 2.276744842529297,48.874651226434366 | + | intermediates | 2.4161338806152344,48.84958044593267\|2.3285865783691406,48.819072029824866 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the road should be similar to "../../data/bduni/common/boucle.json" + + Scenario: [GET] [simple/1.0.0] Route qui fait du sur place + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | start | 2.276744842529297,48.874651226434366 | + | end | 2.276744842529297,48.874651226434366 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the road should be similar to "../../data/bduni/common/point.json" + + Scenario: [GET] [simple/1.0.0] Route à sens unique en sens direct + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | start | 2.3266366124153137,48.87538156341441 | + | end | 2.3267331719398494,48.87491231430877 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the road should be similar to "../../data/bduni/common/unique-direct.json" + + Scenario: [GET] [simple/1.0.0] Routes à sens unique en sens inverse + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | start | 2.326878011226654,48.87438308071262 | + | end | 2.3266473412513733,48.875370978896655 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the road should be similar to "../../data/bduni/common/unique-inverse.json" + + Scenario: [GET] [simple/1.0.0] Route qui doit passer sur un pont mais en interdisant les ponts + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | start | 2.3491859436035156,48.857111116502665 | + | end | 2.347555160522461,48.85404215643837 | + | constraints | {"constraintType":"banned","key":"wayType","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 404 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "error.message" with value "No path found" + diff --git a/test/functional/request/cucumber/features/requestDataTest.feature b/test/functional/request/cucumber/features/req-data-bduni-pgr.feature similarity index 87% rename from test/functional/request/cucumber/features/requestDataTest.feature rename to test/functional/request/cucumber/features/req-data-bduni-pgr.feature index fee32ef..4b87879 100644 --- a/test/functional/request/cucumber/features/requestDataTest.feature +++ b/test/functional/request/cucumber/features/req-data-bduni-pgr.feature @@ -1,13 +1,12 @@ -# Tests fonctionnels de Road2 prenant en compte la donnée -Feature: Road2 with data - Tests fonctionnels de Road2 prenant en compte la donnée +Feature: Road2 with Bduni data via PGR + Tests fonctionnels de Road2 prenant en compte la donnée Bduni via PGR Background: Given I have loaded all my test configuration in "../../configurations/local.json" Scenario Outline: [] [simple/1.0.0] Route normale Given an "" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" + And with default parameters for "route-pgr" And with query parameters: | key | value | | start | 2.3420083522796626,48.845718486262086 | @@ -16,7 +15,7 @@ Feature: Road2 with data Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road - And the road should be similar to "../../data/1.json" + And the road should be similar to "../../data/bduni/common/normale.json" Examples: | method | @@ -25,7 +24,7 @@ Feature: Road2 with data Scenario: [GET] [simple/1.0.0] Route qui doit passer sur un pont mais en interdisant les ponts Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" + And with default parameters for "route-pgr" And with query parameters: | key | value | | start | 2.3491859436035156,48.857111116502665 | @@ -36,7 +35,7 @@ Feature: Road2 with data And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "No path found" - Scenario: [GET] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour (voir ticket #36883) + Scenario: [GET] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: @@ -48,9 +47,9 @@ Feature: Road2 with data Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road - And the road should be similar to "../../data/2.json" + And the road should be similar to "../../data/bduni/pgr/1.json" - Scenario: [POST] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour (voir ticket #36883) + Scenario: [POST] [simple/1.0.0] Route avec point intermediaire qui provoque un demi-tour Given an "POST" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: @@ -64,10 +63,10 @@ Feature: Road2 with data Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road - And the road should be similar to "../../data/2.json" + And the road should be similar to "../../data/bduni/pgr/1.json" - Scenario Outline: [] [simple/1.0.0] Route commençant sur une raquette (voir ticket #37674) + Scenario Outline: [] [simple/1.0.0] Route commençant sur une raquette Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: @@ -78,7 +77,7 @@ Feature: Road2 with data Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road - And the road should be similar to "../../data/3.json" + And the road should be similar to "../../data/bduni/pgr/2.json" Examples: | method | diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature new file mode 100644 index 0000000..870b1d9 --- /dev/null +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature @@ -0,0 +1,52 @@ +Feature: Road2 + Tests fonctionnels de Road2 sur les requêtes de l'API simple 1.0.0 + + Background: + Given I have loaded all my test configuration in "../../configurations/local.json" + + Scenario Outline: [] Route principale + Given an "" request on "/" + When I send the request + Then the server should send a response with status 200 + And the response should contain "Road2" + + Examples: + | method | + | GET | + | POST | + + Scenario: API simple 1.0.0 + Given an "GET" request on "/simple/1.0.0/" + When I send the request + Then the server should send a response with status 200 + And the response should contain "Road2 via l'API simple 1.0.0" + + Scenario: GetCapabilities sur l'API simple 1.0.0 + Given an "GET" request on operation "getcapabilities" in api "simple" "1.0.0" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "info.name" with value "Road2" + And the response should contain an attribute "api.name" with value "simple" + And the response should contain an attribute "api.version" with value "1.0.0" + And the response should contain an attribute "operations.[0].id" with value "route" + And the response should contain an attribute "operations.[0].methods.[1]" with value "POST" + And the response should contain an attribute "operations.[0].parameters.[0].name" with value "resource" + And the response should contain an attribute "operations.[1].id" with value "isochrone" + And the response should contain an attribute "operations.[1].methods.[0]" with value "GET" + And the response should contain an attribute "operations.[1].parameters.[1].name" with value "point" + + Scenario: GetCapabilities sur l'API simple 1.0.0 sans changer le host + Given an "GET" request on operation "getcapabilities" in api "simple" "1.0.0" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "info.url" with configuration value of "url" + + Scenario: GetCapabilities sur l'API simple 1.0.0 en changeant le host + Given an "GET" request on operation "getcapabilities" in api "simple" "1.0.0" + And with the alternative url + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "info.url" with configuration value of "alternativeParameters.url" diff --git a/test/functional/request/cucumber/features/requestTest.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature similarity index 81% rename from test/functional/request/cucumber/features/requestTest.feature rename to test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature index 2efe4a7..c259ad2 100644 --- a/test/functional/request/cucumber/features/requestTest.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature @@ -1,56 +1,10 @@ -# Tests fonctionnels de Road2 -Feature: Road2 - Tests fonctionnels de Road2 +# Tests fonctionnels complémentaires de Road2 +Feature: Road2-Osrm + Tests fonctionnels complémentaires de Road2 sur OSRM Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local.json" - Scenario Outline: [] Route principale - Given an "" request on "/" - When I send the request - Then the server should send a response with status 200 - And the response should contain "Road2" - - Examples: - | method | - | GET | - | POST | - - Scenario: API simple 1.0.0 - Given an "GET" request on "/simple/1.0.0/" - When I send the request - Then the server should send a response with status 200 - And the response should contain "Road2 via l'API simple 1.0.0" - - Scenario: GetCapabilities sur l'API simple 1.0.0 - Given an "GET" request on operation "getcapabilities" in api "simple" "1.0.0" - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain an attribute "info.name" with value "Road2" - And the response should contain an attribute "api.name" with value "simple" - And the response should contain an attribute "api.version" with value "1.0.0" - And the response should contain an attribute "operations.[0].id" with value "route" - And the response should contain an attribute "operations.[0].methods.[1]" with value "POST" - And the response should contain an attribute "operations.[0].parameters.[0].name" with value "resource" - And the response should contain an attribute "operations.[1].id" with value "isochrone" - And the response should contain an attribute "operations.[1].methods.[0]" with value "GET" - And the response should contain an attribute "operations.[1].parameters.[1].name" with value "point" - - Scenario: GetCapabilities sur l'API simple 1.0.0 sans changer le host - Given an "GET" request on operation "getcapabilities" in api "simple" "1.0.0" - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain an attribute "info.url" with configuration value of "url" - - Scenario: GetCapabilities sur l'API simple 1.0.0 en changeant le host - Given an "GET" request on operation "getcapabilities" in api "simple" "1.0.0" - And with the alternative url - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain an attribute "info.url" with configuration value of "alternativeParameters.url" Scenario Outline: [] Route sur l'API simple 1.0.0 Given an "" request on operation "route" in api "simple" "1.0.0" @@ -976,6 +930,204 @@ Scenario Outline: [] Route sur l'API simple 1.0.0 avec geometryFormat=po And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'start' is invalid" + + Scenario Outline: [] Route sur l'API simple 1.0.0 + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + + Examples: + | method | + | GET | + | POST | + + Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | waysAttributes | name \| nature \| importance | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.name" + And the response should contain an attribute "portions.[0].steps.[0].attributes.nature" + And the response should contain an attribute "portions.[0].steps.[0].attributes.importance" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters for "waysAttributes": + | value | + | name | + | nature | + | importance | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.name" + And the response should contain an attribute "portions.[0].steps.[0].attributes.nature" + And the response should contain an attribute "portions.[0].steps.[0].attributes.importance" + + Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | waysAttributes | name \| nature \| importance \| cleabs \| urbain \| access_pieton \| cpx_numero \| largeur_de_chaussee \| position_par_rapport_au_sol \| restriction_de_hauteur \| sens_de_circulation | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'waysAttributes' is invalid" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters for "waysAttributes": + | value | + | name | + | nature | + | importance | + | cleabs | + | urbain | + | access_pieton | + | cpx_numero | + | largeur_de_chaussee | + | position_par_rapport_au_sol | + | restriction_de_hauteur | + | sens_de_circulation | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'waysAttributes' is invalid" + + Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource OSRM + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "constraints.[1].key" + + Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes dont une invalide sur une ressource OSRM + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'constraints' is invalid" + + Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes sur une ressource OSRM + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "constraints.[1].key" + + Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont une invalide sur une ressource OSRM + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'constraints' is invalid" + + Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont deux invalides sur une ressource OSRM + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test1"}\|{"constraintType":"test2","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'constraints' is invalid" + + Scenario: [POST] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource OSRM + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters of object for "constraints": + | value | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "constraints.[1].key" + + Scenario: [POST] Route sur l'API simple 1.0.0 avec deux contraintes dont une ivalide sur une ressource OSRM + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters of object for "constraints": + | value | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'constraints' is invalid" + + Scenario: [POST] Route sur l'API simple 1.0.0 avec trois contraintes sur une ressource OSRM + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters of object for "constraints": + | value | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "constraints.[2].key" + + Scenario: [POST] Route sur l'API simple 1.0.0 avec trois contraintes dont une invalide sur une ressource OSRM + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters of object for "constraints": + | value | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'constraints' is invalid" + + Scenario: [POST] Route sur l'API simple 1.0.0 avec trois contraintes dont deux invalides sur une ressource OSRM + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with table parameters of object for "constraints": + | value | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"test1"} | + | {"constraintType":"test2","key":"waytype","operator":"=","value":"tunnel"} | + | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'constraints' is invalid" + Scenario Outline: [] Nearest sur l'API simple 1.0.0 Given an "" request on operation "nearest" in api "simple" "1.0.0" And with default parameters for "nearest-osrm" @@ -1125,4 +1277,4 @@ Scenario Outline: [] Nearest sur l'API simple 1.0.0 avec un autre crs Examples: | method | | GET | - | POST | \ No newline at end of file + | POST | diff --git a/test/functional/request/cucumber/features/requestComplementTest.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature similarity index 79% rename from test/functional/request/cucumber/features/requestComplementTest.feature rename to test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index 684ed00..218f088 100644 --- a/test/functional/request/cucumber/features/requestComplementTest.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -5,19 +5,6 @@ Feature: Road2-complement Background: Given I have loaded all my test configuration in "../../configurations/local.json" - Scenario Outline: [] Route sur l'API simple 1.0.0 - Given an "" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - - Examples: - | method | - | GET | - | POST | - Scenario Outline: [] Route sur l'API simple 1.0.0 Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" @@ -31,190 +18,6 @@ Feature: Road2-complement | GET | | POST | - Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | waysAttributes | name \| nature \| importance | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - And the response should contain an attribute "portions.[0].steps.[0].attributes.name" - And the response should contain an attribute "portions.[0].steps.[0].attributes.nature" - And the response should contain an attribute "portions.[0].steps.[0].attributes.importance" - - Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters for "waysAttributes": - | value | - | name | - | nature | - | importance | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - And the response should contain an attribute "portions.[0].steps.[0].attributes.name" - And the response should contain an attribute "portions.[0].steps.[0].attributes.nature" - And the response should contain an attribute "portions.[0].steps.[0].attributes.importance" - - Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | waysAttributes | name \| nature \| importance \| cleabs \| urbain \| access_pieton \| cpx_numero \| largeur_de_chaussee \| position_par_rapport_au_sol \| restriction_de_hauteur \| sens_de_circulation | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'waysAttributes' is invalid" - - Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec plusieurs waysAttributes - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters for "waysAttributes": - | value | - | name | - | nature | - | importance | - | cleabs | - | urbain | - | access_pieton | - | cpx_numero | - | largeur_de_chaussee | - | position_par_rapport_au_sol | - | restriction_de_hauteur | - | sens_de_circulation | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'waysAttributes' is invalid" - - Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource OSRM - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - And the response should contain an attribute "constraints.[1].key" - - Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes dont une invalide sur une ressource OSRM - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'constraints' is invalid" - - Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes sur une ressource OSRM - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - And the response should contain an attribute "constraints.[1].key" - - Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont une invalide sur une ressource OSRM - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'constraints' is invalid" - - Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont deux invalides sur une ressource OSRM - Given an "GET" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with query parameters: - | key | value | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test1"}\|{"constraintType":"test2","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'constraints' is invalid" - - Scenario: [POST] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource OSRM - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters of object for "constraints": - | value | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - And the response should contain an attribute "constraints.[1].key" - - Scenario: [POST] Route sur l'API simple 1.0.0 avec deux contraintes dont une ivalide sur une ressource OSRM - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters of object for "constraints": - | value | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'constraints' is invalid" - - Scenario: [POST] Route sur l'API simple 1.0.0 avec trois contraintes sur une ressource OSRM - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters of object for "constraints": - | value | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road - And the response should contain an attribute "constraints.[2].key" - - Scenario: [POST] Route sur l'API simple 1.0.0 avec trois contraintes dont une invalide sur une ressource OSRM - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters of object for "constraints": - | value | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'constraints' is invalid" - - Scenario: [POST] Route sur l'API simple 1.0.0 avec trois contraintes dont deux invalides sur une ressource OSRM - Given an "POST" request on operation "route" in api "simple" "1.0.0" - And with default parameters for "route-osrm" - And with table parameters of object for "constraints": - | value | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"test1"} | - | {"constraintType":"test2","key":"waytype","operator":"=","value":"tunnel"} | - | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request - Then the server should send a response with status 400 - And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'constraints' is invalid" - Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" diff --git a/test/functional/request/cucumber/features/support/world.js b/test/functional/request/cucumber/features/support/world.js index 6912433..5d40a0c 100644 --- a/test/functional/request/cucumber/features/support/world.js +++ b/test/functional/request/cucumber/features/support/world.js @@ -317,7 +317,12 @@ class road2World { getConfigurationValueof(key) { - return this.getJsonContentByKey(this._configuration, key); + let response = this.getJsonContentByKey(this._configuration, key); + if (response.status === "found") { + return response.content; + } else { + return false; + } } @@ -334,14 +339,19 @@ class road2World { responseJSON = this._response; } - let jsonValue = this.getJsonContentByKey(responseJSON, key); - - if (jsonValue.includes(value)) { - return true; + let result = this.getJsonContentByKey(responseJSON, key); + if (result.status === "found") { + if (result.content.includes(value)) { + return true; + } else { + return false; + } } else { return false; } + + } catch(error) { console.log(error); return false; @@ -366,9 +376,8 @@ class road2World { responseJSON = this._response; } - let jsonValue = this.getJsonContentByKey(responseJSON, key); - - if (jsonValue) { + let result = this.getJsonContentByKey(responseJSON, key); + if (result.status === "found") { return true; } else { return false; @@ -397,10 +406,13 @@ class road2World { responseJSON = this._response; } - let jsonValue = this.getJsonContentByKey(responseJSON, key); - - if (typeof jsonValue === "string") { - return true; + let result = this.getJsonContentByKey(responseJSON, key); + if (result.status = "found") { + if (typeof result.content === "string") { + return true; + } else { + return false; + } } else { return false; } @@ -417,6 +429,11 @@ class road2World { getJsonContentByKey(jsonContent, key) { + let returnObject = { + status: "not found", + content: {} + }; + let keysTable = new Array(); keysTable = key.split('.'); @@ -424,19 +441,30 @@ class road2World { let tmpJson = jsonContent; for (let i = 0; i < keysTable.length; i++) { - tmpJson = this.getJsonContentByKeyLO(tmpJson, keysTable[i]); + let response = this.getJsonContentByKeyLO(tmpJson, keysTable[i]); // Gérer le cas vide - if (tmpJson === false) { - return false; + if (response.status === "not found") { + returnObject.status = "not found"; + returnObject.content = {}; + return returnObject; + } else { + returnObject.status = "found"; + returnObject.content = response.content; + tmpJson = response.content; } } - // à enlever - return tmpJson; + + return returnObject; } getJsonContentByKeyLO(jsonContent, key) { + let returnObject = { + status: "not found", + content: {} + }; + // Gérer les tableaux if (Array.isArray(jsonContent)) { @@ -446,90 +474,94 @@ class road2World { let indiceNum = parseInt(indiceTable[0]); if (jsonContent.length > indiceNum) { - return jsonContent[indiceNum]; + returnObject.status = "found"; + returnObject.content = jsonContent[indiceNum]; + return returnObject; } else { - return false; + return returnObject; } } else { - return false; + return returnObject; } } else { for (let tmpKey in jsonContent) { if (tmpKey === key) { - return jsonContent[tmpKey]; + returnObject.status = "found"; + returnObject.content = jsonContent[tmpKey]; + return returnObject; } } } - return false; + return returnObject; } checkCompleteRoad() { if (!this.checkResponseAttribut("resource")) { - return false; + return 1; } if (!this.checkResponseAttribut("profile")) { - return false; + return 2; } if (!this.checkResponseAttribut("optimization")) { - return false; + return 3; } if (!this.checkResponseAttribut("distanceUnit")) { - return false; + return 4; } if (!this.checkResponseAttribut("timeUnit")) { - return false; + return 5; } if (!this.checkResponseAttribut("crs")) { - return false; + return 6; } if (!this.checkResponseAttribut("geometry")) { - return false; + return 7; } if (!this.checkResponseAttribut("bbox")) { - return false; + return 8; } if (!this.checkResponseAttribut("distance")) { - return false; + return 9; } if (!this.checkResponseAttribut("duration")) { - return false; + return 10; } if (!this.checkResponseAttribut("portions.[0]")) { - return false; + return 11; } if (!this.checkResponseAttribut("portions.[0].start")) { - return false; + return 12; } if (!this.checkResponseAttribut("portions.[0].end")) { - return false; + return 13; } if (!this.checkResponseAttribut("portions.[0].distance")) { - return false; + return 14; } if (!this.checkResponseAttribut("portions.[0].duration")) { - return false; + return 15; } if (!this.checkResponseAttribut("portions.[0].steps.[0]")) { - return false; + return 16; } if (!this.checkResponseAttribut("portions.[0].steps.[0].geometry")) { - return false; + return 17; } if (!this.checkResponseAttribut("portions.[0].steps.[0].attributes")) { - return false; + return 18; } if (!this.checkResponseAttribut("portions.[0].steps.[0].distance")) { - return false; + return 19; } if (!this.checkResponseAttribut("portions.[0].steps.[0].duration")) { - return false; + return 20; } return true; @@ -539,31 +571,31 @@ class road2World { checkCompleteIso() { if (!this.checkResponseAttribut("point")) { - return false; + return 1; } if (!this.checkResponseAttribut("resource")) { - return false; + return 2; } if (!this.checkResponseAttribut("resourceVersion")) { - return false; + return 3; } if (!this.checkResponseAttribut("profile")) { - return false; + return 4; } if (!this.checkResponseAttribut("costType")) { - return false; + return 5; } if (!this.checkResponseAttribut("costValue")) { - return false; + return 6; } if (!this.checkResponseAttribut("crs")) { - return false; + return 7; } if (!this.checkResponseAttribut("geometry")) { - return false; + return 8; } if (!this.checkResponseAttribut("direction")) { - return false; + return 9; } return true; @@ -606,6 +638,8 @@ class road2World { let refDurationMin = 0; let refDurationMax = 0; let responseJSON = {}; + let tablePoint = new Array(); + let curPoint = {}; if (typeof this._response === "string") { responseJSON = JSON.parse(this._response); @@ -638,15 +672,25 @@ class road2World { return "Duration is out of boundaries"; } - let bufferStart = turf.buffer(turf.point(referenceRoad.start.split(",")), 0.01, {units: "kilometers"}); + tablePoint = referenceRoad.start.split(","); + curPoint = turf.point([Number(tablePoint[0]), Number(tablePoint[1])]); + let bufferStart = turf.buffer(curPoint, 0.01, {units: "kilometers"}); - if (!turf.booleanWithin(turf.point(responseJSON.start.split(",")), bufferStart)) { + tablePoint = responseJSON.start.split(","); + curPoint = turf.point([Number(tablePoint[0]), Number(tablePoint[1])]); + + if (!turf.booleanWithin(curPoint, bufferStart)) { return "Start is out of the buffer"; } - let bufferEnd = turf.buffer(turf.point(referenceRoad.end.split(",")), 0.01, {units: "kilometers"}); + tablePoint = referenceRoad.end.split(","); + curPoint = turf.point([Number(tablePoint[0]), Number(tablePoint[1])]); + let bufferEnd = turf.buffer(curPoint, 0.01, {units: "kilometers"}); + + tablePoint = responseJSON.end.split(","); + curPoint = turf.point([Number(tablePoint[0]), Number(tablePoint[1])]); - if (!turf.booleanWithin(turf.point(responseJSON.end.split(",")), bufferEnd)) { + if (!turf.booleanWithin(curPoint, bufferEnd)) { return "End is out of the buffer"; } From a5db1fe6532a0d566c44b77a0ee0d10dbdac7013 Mon Sep 17 00:00:00 2001 From: Loic Date: Wed, 20 Apr 2022 18:15:27 +0200 Subject: [PATCH 05/93] test: refactoring et ajout de tests sur turfJS --- src/js/geometry/polygon.js | 46 ++++--- .../features/req-simple-1.0.0-osrm.feature | 3 +- .../features/req-simple-1.0.0-pgr.feature | 81 +++--------- .../req-simple-1.0.0-smartrouting.feature | 84 ++++++++++++ .../mocha/geometry/integrationPolygon.js | 58 +++++++-- test/unit/mocha/npm/testsTurf.js | 123 ++++++++++++++++++ 6 files changed, 299 insertions(+), 96 deletions(-) create mode 100644 test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature create mode 100644 test/unit/mocha/npm/testsTurf.js diff --git a/src/js/geometry/polygon.js b/src/js/geometry/polygon.js index f185ece..e04c5d0 100644 --- a/src/js/geometry/polygon.js +++ b/src/js/geometry/polygon.js @@ -65,44 +65,48 @@ module.exports = class Polygon extends Geometry { * */ _convertGeometry (geom, srcFormat, outFormat) { - // Création d'un objet Polygon depuis les coordonées reçues. - const polygon = turf.polygon(geom); if (srcFormat === outFormat) { return geom; } else if (srcFormat === "polyline" && outFormat === "geojson") { - return polyline.toGeoJSON(polygon); // À tester.. + let tmpPolygon = polyline.toGeoJSON(geom); + tmpPolygon.type = "Polygon"; + return tmpPolygon; } else if (srcFormat === "geojson" && outFormat === "polyline") { + let result = []; if (geom.type === "Point") { // Cas où l'isochrone est un simple point result = polyline.encode([geom.coordinates]); return result - } - if (geom.type === "LineString") { - // Cas où l'isochrone est un simple point + } else if (geom.type === "LineString") { + // Cas où l'isochrone est une line result = polyline.encode(geom.coordinates); return result - } + } else { + + // Conversion du polygone en (Multi)LineString. + let polygon = turf.polygon(geom.coordinates); + const lines = turf.polygonToLine(polygon); + + if (lines.geometry.type === "LineString") { + // Une seule ligne, nous convertissons donc directement. + result = polyline.fromGeoJSON(lines); + } else if (lines.geometry.type === "MultiLineString") { + // Plusieurs lignes, convertion ligne par ligne. + lines.geometry.coordinates.forEach( function(element) { + result.push(polyline.fromGeoJSON({ + "type": "LineString", + "coordinates": element + })); + }); + } - // Conversion du polygone en (Multi)LineString. - const lines = turf.polygonToLine(polygon.geometry.coordinates); + return result; - if (lines.geometry.type === "LineString") { - // Une seule ligne, nous convertissons donc directement. - result = polyline.fromGeoJSON(lines); - } else if (lines.geometry.type === "MultiLineString") { - // Plusieurs lignes, convertion ligne par ligne. - lines.geometry.coordinates.forEach( function(element) { - result.push(polyline.fromGeoJSON({ - "type": "LineString", - "coordinates": element - })); - }); } - return result; } else { //TODO: voir si on peut remplacer ce throw par un return {} throw errorManager.createError("Unsupported geometry conversion"); diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature index c259ad2..41d1d95 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature @@ -1,5 +1,4 @@ -# Tests fonctionnels complémentaires de Road2 -Feature: Road2-Osrm +Feature: Road2-OSRM Tests fonctionnels complémentaires de Road2 sur OSRM Background: diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index 218f088..aa73d59 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -1,6 +1,5 @@ -# Tests fonctionnels complémentaires de Road2 -Feature: Road2-complement - Tests fonctionnels complémentaires de Road2 +Feature: Road2-PGR + Tests fonctionnels complémentaires de Road2 via PGRouting Background: Given I have loaded all my test configuration in "../../configurations/local.json" @@ -737,6 +736,21 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 | GET | | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un geometryFormat polyline + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone" + And with query parameters: + | key | value | + | geometryFormat | polyline | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + + Examples: + | method | + | GET | + | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais distanceUnit Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" @@ -810,64 +824,3 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And the response should contain a complete and valid iso And the response should contain an attribute "constraints.[0].key" - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource smartpgr avec appel à la source smartrouting - Given an "" request on operation "isochrone" in api "simple" "1.0.0" - And with default parameters for "isochrone-smartpgr" - And with query parameters: - | key | value | - | costType | distance | - | costValue | 31000 | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid iso - - Examples: - | method | - | GET | - | POST | - - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource smartpgr avec appel à la source pgr - Given an "" request on operation "isochrone" in api "simple" "1.0.0" - And with default parameters for "isochrone-smartpgr" - And with query parameters: - | key | value | - | costType | distance | - | costValue | 1000 | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid iso - - Examples: - | method | - | GET | - | POST | - - Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource smartpgr avec appel à la source smartrouting - Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" - And with default parameters for "isochrone-smartpgr" - And with query parameters: - | key | value | - | costType | distance | - | costValue | 1000 | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid iso - And the response should contain an attribute "constraints.[0].key" - - Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource smartpgr avec appel à la source pgr - Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" - And with default parameters for "isochrone-smartpgr" - And with query parameters: - | key | value | - | costType | distance | - | costValue | 31000 | - | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | - When I send the request - Then the server should send a response with status 200 - And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid iso - And the response should contain an attribute "constraints.[0].key" \ No newline at end of file diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature new file mode 100644 index 0000000..9197368 --- /dev/null +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature @@ -0,0 +1,84 @@ +Feature: Road2-SMARTROUTING + Tests fonctionnels complémentaires de Road2 via SmartRouting + + Background: + Given I have loaded all my test configuration in "../../configurations/local.json" + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource smartpgr avec appel à la source smartrouting + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-smartpgr" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 31000 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + + Examples: + | method | + | GET | + | POST | + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource smartpgr avec appel à la source pgr + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-smartpgr" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 1000 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + + Examples: + | method | + | GET | + | POST | + + Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource smartpgr avec appel à la source smartrouting + Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-smartpgr" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 1000 | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the response should contain an attribute "constraints.[0].key" + + Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource smartpgr avec appel à la source pgr + Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-smartpgr" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 31000 | + | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the response should contain an attribute "constraints.[0].key" + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un geometryFormat polyline à partir d'une géométrie générée par smartrouting + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-smartpgr" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 31000 | + | geometryFormat | polyline | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + + Examples: + | method | + | GET | + | POST | \ No newline at end of file diff --git a/test/integration/mocha/geometry/integrationPolygon.js b/test/integration/mocha/geometry/integrationPolygon.js index 9a30dfe..1f4f10e 100644 --- a/test/integration/mocha/geometry/integrationPolygon.js +++ b/test/integration/mocha/geometry/integrationPolygon.js @@ -10,13 +10,22 @@ describe('Test de la classe Polygon', function() { logManager.manageLogs(); }); - let refGeom = {"type":"Polygon","coordinates":[[[2.32689460427517,48.8796664760288],[2.32472468908434,48.8806534168629],[2.32338130915736,48.8825632112067],[2.32206953790069,48.882561186561],[2.32095428209082,48.8823381270098],[2.32092436698872,48.8823316620015],[2.32242419764372,48.8840192132601],[2.3245085041333,48.884600267544],[2.32477614691189,48.8847825427569],[2.32501420082458,48.8849331739511],[2.32574230742436,48.88715232559],[2.32559587720714,48.8875076350737],[2.32576801732307,48.8880104297148],[2.32715614178612,48.8871800641188],[2.3288567997417,48.8857983467344],[2.33075204345075,48.8853970623475],[2.33185424388145,48.8885589613481],[2.33180026632535,48.888726834289],[2.3317775934945,48.8887932579532],[2.33337839987913,48.889453293149],[2.33574382909869,48.8895123042427],[2.33701742654633,48.8879203602709],[2.33750337619062,48.8876703390523],[2.33909347535874,48.8868130741082],[2.33976160902747,48.8846691634537],[2.34172981868777,48.8834479189961],[2.34386348025055,48.8829407175301],[2.34433279903902,48.8830305236972],[2.34443888269128,48.8830526885388],[2.34468547821561,48.8828534878423],[2.34474999949318,48.8827108474226],[2.34255154287428,48.8811150723814],[2.34044822598183,48.8804155140435],[2.33984157112535,48.8788994912569],[2.33737131803045,48.8776861309969],[2.33709317504751,48.8767214024118],[2.33535461098932,48.8761991404349],[2.3327040299337,48.8742867916612],[2.3314074826543,48.8750269072541],[2.32956775219663,48.8769708782012],[2.32700731273326,48.8795969615579],[2.32689460427517,48.8796664760288]]]}; - let refPolyline = "}xiiHaneMcEpL}JjG?dGj@~E@DqIkHsBaLc@u@]m@{LqCgAZcBa@dDuGrGsInAyJwR{Ea@HKBcC_IKwM|H_Gp@_BjD}HjLeCrFiKdBiLQ}ACUf@q@ZK|HvLjCbLnHxBpFlN`Ev@fBzI|JpOsC`GcKnJmO~NMV"; + let refGeomPolygon = {"type":"Polygon","coordinates":[[[2.32689460427517,48.8796664760288],[2.32472468908434,48.8806534168629],[2.32338130915736,48.8825632112067],[2.32206953790069,48.882561186561],[2.32095428209082,48.8823381270098],[2.32092436698872,48.8823316620015],[2.32242419764372,48.8840192132601],[2.3245085041333,48.884600267544],[2.32477614691189,48.8847825427569],[2.32501420082458,48.8849331739511],[2.32574230742436,48.88715232559],[2.32559587720714,48.8875076350737],[2.32576801732307,48.8880104297148],[2.32715614178612,48.8871800641188],[2.3288567997417,48.8857983467344],[2.33075204345075,48.8853970623475],[2.33185424388145,48.8885589613481],[2.33180026632535,48.888726834289],[2.3317775934945,48.8887932579532],[2.33337839987913,48.889453293149],[2.33574382909869,48.8895123042427],[2.33701742654633,48.8879203602709],[2.33750337619062,48.8876703390523],[2.33909347535874,48.8868130741082],[2.33976160902747,48.8846691634537],[2.34172981868777,48.8834479189961],[2.34386348025055,48.8829407175301],[2.34433279903902,48.8830305236972],[2.34443888269128,48.8830526885388],[2.34468547821561,48.8828534878423],[2.34474999949318,48.8827108474226],[2.34255154287428,48.8811150723814],[2.34044822598183,48.8804155140435],[2.33984157112535,48.8788994912569],[2.33737131803045,48.8776861309969],[2.33709317504751,48.8767214024118],[2.33535461098932,48.8761991404349],[2.3327040299337,48.8742867916612],[2.3314074826543,48.8750269072541],[2.32956775219663,48.8769708782012],[2.32700731273326,48.8795969615579],[2.32689460427517,48.8796664760288]]]}; + let refPolylinePolygon = "}xiiHaneMcEpL}JjG?dGj@~E@DqIkHsBaLc@u@]m@{LqCgAZcBa@dDuGrGsInAyJwR{Ea@HKBcC_IKwM|H_Gp@_BjD}HjLeCrFiKdBiLQ}ACUf@q@ZK|HvLjCbLnHxBpFlN`Ev@fBzI|JpOsC`GcKnJmO~NMV"; + let newGeomPolygon = {"type":"Polygon","coordinates":[[2.32689,48.87967],[2.32472,48.88065],[2.32338,48.88256],[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32478,48.88478],[2.32501,48.88493],[2.32574,48.88715],[2.3256,48.88751],[2.32577,48.88801],[2.32716,48.88718],[2.32886,48.8858],[2.33075,48.8854],[2.33185,48.88856],[2.3318,48.88873],[2.33178,48.88879],[2.33338,48.88945],[2.33574,48.88951],[2.33702,48.88792],[2.3375,48.88767],[2.33909,48.88681],[2.33976,48.88467],[2.34173,48.88345],[2.34386,48.88294],[2.34433,48.88303],[2.34444,48.88305],[2.34469,48.88285],[2.34475,48.88271],[2.34255,48.88112],[2.34045,48.88042],[2.33984,48.8789],[2.33737,48.87769],[2.33709,48.87672],[2.33535,48.8762],[2.3327,48.87429],[2.33141,48.87503],[2.32957,48.87697],[2.32701,48.8796],[2.32689,48.87967]]}; + + let refGeomLine = {"type":"LineString","coordinates":[[2.32689460427517,48.8796664760288],[2.32472468908434,48.8806534168629],[2.32338130915736,48.8825632112067]]}; + let refPolylineLine = "aneM}xiiHpLcEjG}J"; + let newGeomLine = {"type":"Polygon","coordinates":[[48.87967,2.32689],[48.88065,2.32472],[48.88256,2.32338]]}; + + let refGeomPoint = {"type":"Point","coordinates":[2.32689460427517,48.8796664760288]}; + let refPolylinePoint = "aneM}xiiH"; + let newGeomPoint = {"type":"Polygon","coordinates":[[48.87967,2.32689]]}; - let polygon = new Polygon(refGeom, "geojson", "EPSG:4326"); - describe('Test du constructeur et des getters/setters', function() { + let polygon = new Polygon(refGeomPolygon, "geojson", "EPSG:4326"); + it('Get type', function() { assert.equal(polygon.type, "polygon"); }); @@ -26,16 +35,47 @@ describe('Test de la classe Polygon', function() { }); it('Get geom', function() { - assert.equal(polygon.geom, refGeom); + assert.equal(polygon.geom, refGeomPolygon); }); }); - describe('Changer le format', function() { + describe('Changer le format avec getGeometryWithFormat()', function() { + + it('Polygon : geojson to polyline', function() { + let polygon = new Polygon(refGeomPolygon, "geojson", "EPSG:4326"); + let tmpGeom = polygon.getGeometryWithFormat("polyline"); + assert.equal(tmpGeom, refPolylinePolygon); + }); + + it('Polygon : polyline to geojson', function() { + let polygonPoly = new Polygon(refPolylinePolygon, "polyline", "EPSG:4326"); + let tmpGeom = polygonPoly.getGeometryWithFormat("geojson"); + assert.deepEqual(tmpGeom, newGeomPolygon); + }); + + it('Line : geojson to polyline', function() { + let polygon = new Polygon(refGeomLine, "geojson", "EPSG:4326"); + let tmpGeom = polygon.getGeometryWithFormat("polyline"); + assert.equal(tmpGeom, refPolylineLine); + }); + + it('Line : polyline to geojson', function() { + let polygonPoly = new Polygon(refPolylineLine, "polyline", "EPSG:4326"); + let tmpGeom = polygonPoly.getGeometryWithFormat("geojson"); + assert.deepEqual(tmpGeom, newGeomLine); + }); + + it('Point : geojson to polyline', function() { + let polygon = new Polygon(refGeomPoint, "geojson", "EPSG:4326"); + let tmpGeom = polygon.getGeometryWithFormat("polyline"); + assert.equal(tmpGeom, refPolylinePoint); + }); - it('getGeometryWithFormat()', function() { - let tmpPolyline = polygon.getGeometryWithFormat("polyline"); - assert.equal(tmpPolyline, refPolyline); + it('Point : polyline to geojson', function() { + let polygonPoly = new Polygon(refPolylinePoint, "polyline", "EPSG:4326"); + let tmpGeom = polygonPoly.getGeometryWithFormat("geojson"); + assert.deepEqual(tmpGeom, newGeomPoint); }); }); diff --git a/test/unit/mocha/npm/testsTurf.js b/test/unit/mocha/npm/testsTurf.js new file mode 100644 index 0000000..a93e18a --- /dev/null +++ b/test/unit/mocha/npm/testsTurf.js @@ -0,0 +1,123 @@ +const assert = require('assert'); +const logManager = require('../logManager'); +const turf = require('@turf/turf'); + +describe('Test de la dépendance NPM TurfJS', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + describe('Test de la fonction point', function() { + + let refPoint = {"geometry": {"coordinates": [2,48],"type": "Point"},"properties": {},"type": "Feature"}; + + it('Creation d\'un point à partir de nombres', function() { + let point = turf.point([2,48]); + assert.deepEqual(point, refPoint); + }); + + }); + + describe('Test de la fonction bbox', function() { + + let refBbox = [-82, 35, -74, 42]; + + it('Creation d\'une bbox à partir d\'une ligne en geojson ', function() { + let line = turf.lineString([[-74, 40], [-78, 42], [-82, 35]]); + let bbox = turf.bbox(line); + assert.deepEqual(bbox, refBbox); + }); + + }); + + describe('Test de la fonction polygon', function() { + + let refPolygon = {"type":"Feature", "properties":{},"geometry":{"type": "Polygon","coordinates":[[[2.32689,48.87967],[2.32472,48.88065],[2.32338,48.88256],[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32478,48.88478],[2.32501,48.88493],[2.32574,48.88715],[2.3256,48.88751],[2.32577,48.88801],[2.32716,48.88718],[2.32886,48.8858],[2.33075,48.8854],[2.33185,48.88856],[2.3318,48.88873],[2.33178,48.88879],[2.33338,48.88945],[2.33574,48.88951],[2.33702,48.88792],[2.3375,48.88767],[2.33909,48.88681],[2.33976,48.88467],[2.34173,48.88345],[2.34386,48.88294],[2.34433,48.88303],[2.34444,48.88305],[2.34469,48.88285],[2.34475,48.88271],[2.34255,48.88112],[2.34045,48.88042],[2.33984,48.8789],[2.33737,48.87769],[2.33709,48.87672],[2.33535,48.8762],[2.3327,48.87429],[2.33141,48.87503],[2.32957,48.87697],[2.32701,48.8796],[2.32689,48.87967]]]}}; + + it('Creation d\'un polygon à partir des coordonnées d\'un polygon en geojson', function() { + let geojsonCoord = [[[2.32689,48.87967],[2.32472,48.88065],[2.32338,48.88256],[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32478,48.88478],[2.32501,48.88493],[2.32574,48.88715],[2.3256,48.88751],[2.32577,48.88801],[2.32716,48.88718],[2.32886,48.8858],[2.33075,48.8854],[2.33185,48.88856],[2.3318,48.88873],[2.33178,48.88879],[2.33338,48.88945],[2.33574,48.88951],[2.33702,48.88792],[2.3375,48.88767],[2.33909,48.88681],[2.33976,48.88467],[2.34173,48.88345],[2.34386,48.88294],[2.34433,48.88303],[2.34444,48.88305],[2.34469,48.88285],[2.34475,48.88271],[2.34255,48.88112],[2.34045,48.88042],[2.33984,48.8789],[2.33737,48.87769],[2.33709,48.87672],[2.33535,48.8762],[2.3327,48.87429],[2.33141,48.87503],[2.32957,48.87697],[2.32701,48.8796],[2.32689,48.87967]]]; + let polygon = turf.polygon(geojsonCoord); + assert.deepEqual(polygon, refPolygon); + }); + + }); + + describe('Test de la fonction polygonToLine', function() { + + let refLine = {"type":"Feature", "properties":{},"geometry":{"type": "LineString","coordinates":[[2.32689,48.87967],[2.32472,48.88065],[2.32338,48.88256],[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32478,48.88478],[2.32501,48.88493],[2.32574,48.88715],[2.3256,48.88751],[2.32577,48.88801],[2.32716,48.88718],[2.32886,48.8858],[2.33075,48.8854],[2.33185,48.88856],[2.3318,48.88873],[2.33178,48.88879],[2.33338,48.88945],[2.33574,48.88951],[2.33702,48.88792],[2.3375,48.88767],[2.33909,48.88681],[2.33976,48.88467],[2.34173,48.88345],[2.34386,48.88294],[2.34433,48.88303],[2.34444,48.88305],[2.34469,48.88285],[2.34475,48.88271],[2.34255,48.88112],[2.34045,48.88042],[2.33984,48.8789],[2.33737,48.87769],[2.33709,48.87672],[2.33535,48.8762],[2.3327,48.87429],[2.33141,48.87503],[2.32957,48.87697],[2.32701,48.8796],[2.32689,48.87967]]}}; + + it('Creation d\'une ligne à partir des coordonnées d\'un polygon en geojson', function() { + let geojsonCoord = [[[2.32689,48.87967],[2.32472,48.88065],[2.32338,48.88256],[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32478,48.88478],[2.32501,48.88493],[2.32574,48.88715],[2.3256,48.88751],[2.32577,48.88801],[2.32716,48.88718],[2.32886,48.8858],[2.33075,48.8854],[2.33185,48.88856],[2.3318,48.88873],[2.33178,48.88879],[2.33338,48.88945],[2.33574,48.88951],[2.33702,48.88792],[2.3375,48.88767],[2.33909,48.88681],[2.33976,48.88467],[2.34173,48.88345],[2.34386,48.88294],[2.34433,48.88303],[2.34444,48.88305],[2.34469,48.88285],[2.34475,48.88271],[2.34255,48.88112],[2.34045,48.88042],[2.33984,48.8789],[2.33737,48.87769],[2.33709,48.87672],[2.33535,48.8762],[2.3327,48.87429],[2.33141,48.87503],[2.32957,48.87697],[2.32701,48.8796],[2.32689,48.87967]]]; + let polygon = turf.polygon(geojsonCoord); + let line = turf.polygonToLine(polygon); + assert.deepEqual(line, refLine); + }); + + }); + + describe('Test de la fonction lineSlice', function() { + + let line = {"type":"Feature", "properties":{},"geometry":{"type": "LineString","coordinates":[[2.32689,48.87967],[2.32472,48.88065],[2.32338,48.88256],[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32478,48.88478],[2.32501,48.88493],[2.32574,48.88715],[2.3256,48.88751],[2.32577,48.88801],[2.32716,48.88718],[2.32886,48.8858],[2.33075,48.8854],[2.33185,48.88856],[2.3318,48.88873],[2.33178,48.88879],[2.33338,48.88945],[2.33574,48.88951],[2.33702,48.88792],[2.3375,48.88767],[2.33909,48.88681],[2.33976,48.88467],[2.34173,48.88345],[2.34386,48.88294],[2.34433,48.88303],[2.34444,48.88305],[2.34469,48.88285],[2.34475,48.88271],[2.34255,48.88112],[2.34045,48.88042],[2.33984,48.8789],[2.33737,48.87769],[2.33709,48.87672],[2.33535,48.8762],[2.3327,48.87429],[2.33141,48.87503],[2.32957,48.87697],[2.32701,48.8796],[2.32689,48.87967]]}}; + let start = turf.point([2.32207,48.88256]); + let stop = turf.point([2.32451,48.8846]); + let refSliced = {"geometry":{"coordinates":[[2.32207,48.88256],[2.32095,48.88234],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + + it('Découpage d\'une ligne à partir d\'une ligne en geojson', function() { + let lineSliced = turf.lineSlice(start, stop, line); + assert.deepEqual(lineSliced, refSliced); + }); + + }); + + describe('Test de la fonction truncate', function() { + + let line = {"geometry":{"coordinates":[[2.322075785,48.882563257],[2.320957896,48.8823445245],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + let refTrunc = {"geometry":{"coordinates":[[2.322076,48.882563],[2.320958,48.882345],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + + it('Modification de la géométrie par découpage des coordonnées', function() { + let lineTrunc = turf.truncate(line, {precision: 6}); + assert.deepEqual(lineTrunc, refTrunc); + }); + + }); + + describe('Test de la fonction nearestPointOnLine', function() { + + let line = {"geometry":{"coordinates":[[2.322075785,48.882563257],[2.320957896,48.8823445245],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + let point = turf.point([2.32207,48.88256]); + let refPoint = {geometry: {coordinates: [2.3220691120694474,48.88256195133658],type: 'Point'},properties: {dist: 0.00022648508237084762,index: 0,location: 0.0005090817926782273},type: 'Feature'}; + + it('Calcul du point le plus proche d\'un autre sur une ligne ', function() { + let nearest = turf.nearestPointOnLine(line, point,{precision: 6}); + assert.deepEqual(nearest, refPoint); + }); + + }); + + describe('Test de la fonction length', function() { + + let line = {"geometry":{"coordinates":[[2.322075785,48.882563257],[2.320957896,48.8823445245],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + let refLength = 0.4719505233593252; + + it('Calcul du point le plus proche d\'un autre sur une ligne ', function() { + let lengthOfLine = turf.length(line); + assert.equal(lengthOfLine, refLength); + }); + + }); + + describe('Test de la fonction cleanCoords', function() { + + let line = {"geometry":{"coordinates":[[2.322075785,48.882563257],[2.320957896,48.8823445245],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + let refLine = {"geometry":{"coordinates":[[2.322075785,48.882563257],[2.320957896,48.8823445245],[2.32092,48.88233],[2.32242,48.88402],[2.32451,48.8846]],"type":"LineString"},"properties":{},"type":"Feature"}; + + it('Calcul du point le plus proche d\'un autre sur une ligne ', function() { + let cleanedLine = turf.cleanCoords(line); + assert.deepEqual(cleanedLine, refLine); + }); + + }); + +}); From a585ef1fe3afe77d053633f76e19991b69835319 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 26 Sep 2022 11:20:35 +0200 Subject: [PATCH 06/93] creation d'une roadmap pour le projet --- documentation/developers/roadmap.md | 12 ++++++++++++ readme.md | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 documentation/developers/roadmap.md diff --git a/documentation/developers/roadmap.md b/documentation/developers/roadmap.md new file mode 100644 index 0000000..7138fdb --- /dev/null +++ b/documentation/developers/roadmap.md @@ -0,0 +1,12 @@ +# Roadmap du projet Road2 + +## Développements en cours + +Pour la fin de l'année 2022 : + +- Intégration de Valhalla dans Road2 et Route-graph-generator +- Refactorisation d'une partie du code pour : + - une meilleure gestion de la configuration + - la création d'un processus administrateur du service. Ce nouveau processus portera l'API admin sans impacter les requêtes des utilisateurs classiques du service. + + diff --git a/readme.md b/readme.md index 76e3a83..72c7f32 100644 --- a/readme.md +++ b/readme.md @@ -84,6 +84,8 @@ On trouvera dans le dossier [docker/distrubutions](./docker/distributions) diff On trouvera une documentation dédiée aux développeurs [ici](./documentation/developers/readme.md). Elle indique les concepts utiles pour effectuer des développements sur Road2. +Pour en savoir plus sur notre roadmap, vous pouvez regarder ce [document](./documentation/developers/roadmap.md). + De plus, il est possible d'utiliser ce [docker-compose](./docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. ## Utilisation en production From cf39701b06b34a463b04253bea4d6b882ba2e786 Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 20 Oct 2022 14:26:27 +0200 Subject: [PATCH 07/93] Merge : refactoring de la gestion de la configuration et creation d'un processus administrateur --- .vscode/launch.json | 13 +- changelog.md | 10 + docker/config/log4js-administration.json | 18 + .../{log4js.json => log4js-service.json} | 0 docker/config/road2.json | 66 +- docker/config/service.json | 59 + docker/dev/docker-compose.yml | 3 +- documentation/configuration/admin_model.yaml | 94 ++ documentation/configuration/readme.md | 28 +- ...guration_model.yaml => service_model.yaml} | 17 +- documentation/developers/concepts.md | 38 +- documentation/developers/readme.md | 10 +- package.json | 4 +- readme.md | 2 +- src/js/administrator/administrator.js | 479 ++++++ src/js/apis/apisManager.js | 291 +++- src/js/apis/simple/1.0.0/init.js | 2 +- src/js/base/base.js | 4 + src/js/base/baseManager.js | 101 +- src/js/geography/projectionManager.js | 260 +++- src/js/operations/operationManager.js | 384 +++-- src/js/parameters/parameterManager.js | 336 ++-- src/js/resources/osrmResource.js | 26 - src/js/resources/pgrResource.js | 26 - src/js/resources/resource.js | 16 - src/js/resources/resourceManager.js | 405 +++-- src/js/resources/smartpgrResource.js | 26 - src/js/road2.js | 174 ++- src/js/server/serverManager.js | 109 +- src/js/service/main.js | 250 +++ src/js/service/service.js | 816 +++++----- src/js/service/serviceAdministered.js | 55 + src/js/service/serviceManager.js | 130 ++ src/js/service/serviceProcess.js | 118 ++ src/js/sources/osrmSource.js | 5 +- src/js/sources/pgrSource.js | 6 +- src/js/sources/smartroutingSource.js | 4 - src/js/sources/source.js | 31 - src/js/sources/sourceManager.js | 474 +++--- src/js/topology/topologyManager.js | 206 ++- src/js/utils/logManager.js | 91 ++ src/js/utils/processManager.js | 10 +- .../cucumber/features/conf-admin.feature | 351 +++++ .../cucumber/features/conf-common.feature | 51 + .../cucumber/features/conf-cors.feature | 21 + .../cucumber/features/conf-log.feature | 88 ++ .../cucumber/features/conf-operation.feature | 122 ++ .../features/conf-osrm-ressource.feature | 21 + .../cucumber/features/conf-parameter.feature | 307 ++++ .../cucumber/features/conf-projection.feature | 110 ++ .../cucumber/features/conf-service.feature | 677 +++++++++ .../features/configurationTest.feature | 1354 ----------------- .../cucumber/features/support/steps.js | 19 +- .../cucumber/features/support/world.js | 107 +- .../cucumber/configurations/local-admin.json | 17 + .../{local.json => local-service.json} | 6 - .../cucumber/features/req-admin-1.0.0.feature | 2 +- .../features/req-data-bduni-osrm.feature | 2 +- .../features/req-data-bduni-pgr.feature | 2 +- .../features/req-simple-1.0.0-common.feature | 2 +- .../features/req-simple-1.0.0-osrm.feature | 2 +- .../features/req-simple-1.0.0-pgr.feature | 2 +- .../req-simple-1.0.0-smartrouting.feature | 2 +- .../mocha/apis/integrationApiManager.js | 6 +- .../mocha/base/integrationBaseManager.js | 19 +- .../mocha/service/integrationService.js | 2 +- test/integration/readme.md | 4 +- .../mocha/geography/testsProjectionManager.js | 88 +- 68 files changed, 5487 insertions(+), 3094 deletions(-) create mode 100644 docker/config/log4js-administration.json rename docker/config/{log4js.json => log4js-service.json} (100%) create mode 100644 docker/config/service.json create mode 100644 documentation/configuration/admin_model.yaml rename documentation/configuration/{configuration_model.yaml => service_model.yaml} (88%) create mode 100644 src/js/administrator/administrator.js create mode 100644 src/js/service/main.js create mode 100644 src/js/service/serviceAdministered.js create mode 100644 src/js/service/serviceManager.js create mode 100644 src/js/service/serviceProcess.js create mode 100644 src/js/utils/logManager.js create mode 100644 test/functional/configuration/cucumber/features/conf-admin.feature create mode 100644 test/functional/configuration/cucumber/features/conf-common.feature create mode 100644 test/functional/configuration/cucumber/features/conf-cors.feature create mode 100644 test/functional/configuration/cucumber/features/conf-log.feature create mode 100644 test/functional/configuration/cucumber/features/conf-operation.feature create mode 100644 test/functional/configuration/cucumber/features/conf-osrm-ressource.feature create mode 100644 test/functional/configuration/cucumber/features/conf-parameter.feature create mode 100644 test/functional/configuration/cucumber/features/conf-projection.feature create mode 100644 test/functional/configuration/cucumber/features/conf-service.feature delete mode 100644 test/functional/configuration/cucumber/features/configurationTest.feature create mode 100644 test/functional/request/cucumber/configurations/local-admin.json rename test/functional/request/cucumber/configurations/{local.json => local-service.json} (90%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9c54d6c..47ec703 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "configurations": [ { - "name": "Docker: Attach to Node", + "name": "Docker: Attach to Road2 Administrator", "type": "node", "request": "attach", "port": 9229, @@ -14,6 +14,17 @@ "protocol": "inspector", "localRoot": "${workspaceFolder}", "remoteRoot": "/home/docker/app/" + }, + { + "name": "Docker: Attach to Road2 Service", + "type": "node", + "request": "attach", + "port": 9230, + "address": "localhost", + "protocol": "inspector", + "localRoot": "${workspaceFolder}", + "remoteRoot": "/home/docker/app/" } + ] } \ No newline at end of file diff --git a/changelog.md b/changelog.md index dca08fe..49dcf98 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ +# 2.0.0 + +ADDED: + - La classe Administrator permet de gérer le service via une API. Notamment la création, la suppression et la modification d'un service seront possible. + - Cette classe est configurée par un nouveau fichier de configuration. + +CHANGED: + - L'option --configCheck au démarrage de Road2 n'a plus exactement le même comportement. + - Le fichier server.json permet maintenant de configurer l'administrateur et donc n'a plus le même contenu. Ce dernier est dans service.json. + # 1.O.14 FIXED: - Mauvaise ligne pour un log diff --git a/docker/config/log4js-administration.json b/docker/config/log4js-administration.json new file mode 100644 index 0000000..e9bfd49 --- /dev/null +++ b/docker/config/log4js-administration.json @@ -0,0 +1,18 @@ +{ "mainConf": + { + "appenders": { + "console": { "type": "console", "layout": {"type": "pattern", "pattern": "%[[%d] [%p] %c %z -%] %m"} }, + "file": { "type": "file", "filename": "/var/log/road2/road2-admin.log", "layout": {"type": "pattern", "pattern": "[%d] [%p] %c %z - %m%n"} }, + "http": { "type": "file", "filename": "/var/log/road2/access-admin.log"} + }, + "categories": { + "default": { "appenders": ["console","file"], "level": "debug" }, + "request": { "appenders": ["console","http"], "level": "info" } + }, + "disableClustering": true + }, + "httpConf": { + "level": "info", + "format": ":remote-addr - :method :url HTTP/:http-version :status :content-length :referrer :user-agent" + } +} diff --git a/docker/config/log4js.json b/docker/config/log4js-service.json similarity index 100% rename from docker/config/log4js.json rename to docker/config/log4js-service.json diff --git a/docker/config/road2.json b/docker/config/road2.json index b774844..4799842 100644 --- a/docker/config/road2.json +++ b/docker/config/road2.json @@ -1,53 +1,27 @@ { - "application": { - "name": "Road2", - "title": "Service de calcul d'itinéraire", - "description": "Ce service permet de calculer des itinéraires sur les données du Géoportail.", - "url": "https://localhost/", - "provider": { - "name": "IGN", - "site": "www.ign.fr", - "mail": "sav@ign.fr" + "administration":{ + "api" : { + "name" : "admin", + "version" : "1.0.0" }, - "logs": { - "configuration": "/home/docker/config/log4js.json" - }, - "operations":{ - "directory": "/home/docker/app/src/resources/operations", - "parameters": { - "directory": "/home/docker/app/src/resources/parameters" + "services": [ + { + "id": "main", + "configuration": "/home/docker/config/service.json", + "onStart": "true", + "creationType": "newProcess" } - }, - "resources": { - "directories": [ - "/home/docker/data/resources/" - ] - }, - "network": { - "servers": [ - { - "id": "internalServer", - "https": "false", - "host": "0.0.0.0", - "port": "8080" - }, - { - "id": "externalServer", - "https": "true", - "host": "0.0.0.0", - "port": "443", - "options": { - "key": "/run/secrets/key", - "cert": "/run/secrets/cert" - } - } - ], - "cors": { - "configuration": "/home/docker/config/cors.json" + ], + "network" : { + "server" : { + "id": "administrator", + "https": "false", + "host": "0.0.0.0", + "port": "8079" } - }, - "projections": { - "directory": "/home/docker/config/projections/" + }, + "logs": { + "configuration": "/home/docker/config/log4js-administration.json" } } } diff --git a/docker/config/service.json b/docker/config/service.json new file mode 100644 index 0000000..b9c0620 --- /dev/null +++ b/docker/config/service.json @@ -0,0 +1,59 @@ +{ + "application": { + "name": "Road2", + "title": "Service de calcul d'itinéraire", + "description": "Ce service permet de calculer des itinéraires sur les données du Géoportail.", + "url": "https://localhost/", + "provider": { + "name": "IGN", + "site": "www.ign.fr", + "mail": "sav@ign.fr" + }, + "logs": { + "configuration": "/home/docker/config/log4js-service.json" + }, + "operations":{ + "directory": "/home/docker/app/src/resources/operations", + "parameters": { + "directory": "/home/docker/app/src/resources/parameters" + } + }, + "resources": { + "directories": [ + "/home/docker/data/resources/" + ] + }, + "network": { + "servers": [ + { + "id": "internalServer", + "https": "false", + "host": "0.0.0.0", + "port": "8080" + }, + { + "id": "externalServer", + "https": "true", + "host": "0.0.0.0", + "port": "443", + "options": { + "key": "/run/secrets/key", + "cert": "/run/secrets/cert" + } + } + ], + "cors": { + "configuration": "/home/docker/config/cors.json" + } + }, + "projections": { + "directory": "/home/docker/config/projections/" + }, + "apis": [ + { + "name" : "simple", + "version" : "1.0.0" + } + ] + } +} diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index ee365b8..d4756d8 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -11,10 +11,11 @@ services: - pgrouting environment: - NODE_ENV=debug - command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" + command : "sleep 60000" ports: - 8080:8080 - 9229:9229 + - 9230:9230 - 443:443 volumes: - iti-data-volume:/home/docker/data diff --git a/documentation/configuration/admin_model.yaml b/documentation/configuration/admin_model.yaml new file mode 100644 index 0000000..db8b60e --- /dev/null +++ b/documentation/configuration/admin_model.yaml @@ -0,0 +1,94 @@ +# Description d'un fichier de configuration d'administration + +# Une application est un objet qui contient toutes les informations utiles pour que l'application puisse diffuser ses services. +"administration": + type: object + required: true + properties: + # Indication de l'api utilisée pour cette instance. Cela doit correspondre avec un dossier présent dans les APIs du projet + "api": + type: object + required: true + properties: + # Nom de l'api + "name": + type: string + required: true + # Version de l'api + "version": + type: string + required: true + # Configurations des services gérés par l'administrateur + "services": + type: array + required: true + minItems: 1 + items: + type: object + properties: + # Id du service. Utile pour administrer le serivce via l'API + "id": + type: string + required: true + # Configuration du service. Pour le moment, c'est le fichier de configuration du service (service.json) + "configuration": + type: string + required: true + # Indiquer si on souhaite que le service soit démarré au lancement de l'admin + "onStart": + type: string + required: true + # Type de création du service. Pour le moment, il seul 'newProcess' est accepté. On crée un nouveau processus pour le service. + "creationType": + type: string + required: true + # Configuration des logs de l'application + "logs": + type: object + required: true + properties: + # Emplacement du fichier de configuration + "configuration": + type: string + required: true + # Configuration du network + "network": + type: object + required: true + properties: + # Liste des serveurs que l'on veut instancier + "server": + type: object + properties: + # Id du serveur + "id": + type: string + required: true + # Host + "host": + type: string + required: true + # Port + "port": + type: string + required: true + # Utilisation du HTTPS + "https": + type: string + enum: ["true", "false"] + required: true + # Options pour le HTTPS (nécessaire si https="true") + "options": + type: object + required: false + properties: + # Chemin du fichier .key + "key": + type: string + required: true + # Chemin du fichier .cert + "cert": + type: string + required: true + + diff --git a/documentation/configuration/readme.md b/documentation/configuration/readme.md index 06a0a30..ed6e663 100644 --- a/documentation/configuration/readme.md +++ b/documentation/configuration/readme.md @@ -2,13 +2,33 @@ ## Vue globale -Pour instancier Road2, il est nécessaire de lui fournir un certain nombre d'informations. Pour cela, il y a un unique point d'entrée qui est le *server.json*. Ce fichier va indiquer l'emplacement des autres: *log4js.json* pour les logs, le *cors.json* si on souhaite spécifier une politique de CORS, le dossier des *projections* et les dossiers des *ressources*. +Pour instancier Road2, il est nécessaire de lui fournir un certain nombre d'informations. Pour cela, il y a différents JSON. Les fichiers que l'on va remplir vont dépendre de l'usage que l'on veut en faire. Nous allons présenter un usage complet. Ce dernier permettra de comprendre les autres usages. -## server.json +Un usage complet des possibilités offertes par le projet Road2 est le suivant : on veut instancier, dès le démarrage, un administrateur et son service. + +Dans ce cas là, nous aurons pour point d'entrée, le fichier *administration.json*. + +Ce fichier va indiquer plusieurs informations et deux éléments : +- l'emplacement de la configuration des services : un *service.json* par services associés. +- un *log4js.json* pour les logs de l'administrateur + +Chaque *service.json* va indiquer les éléments suivants : +- un *log4js.json* pour les logs de l'administrateur et un par service, +- un *cors.json* par service si on souhaite spécifier une politique de CORS, +- le dossier des *projections*, +- les dossiers des *ressources*. + +## administration.json + +Ce fichier indique quelques informations générales liées à l'instance d'administration. Son principal objectif est l'indication des logs et des services gérés. + +On peut trouver un [exemple](../../docker/config/road2.json) de ce fichier et le [modèle](./admin_model.yaml) au format YAML. + +## service.json Ce fichier indique quelques informations générales liées à l'instance de Road2. Son principal objectif est l'indication des logs et des ressources du serveur. Néanmoins, il permet de préciser beaucoup plus d'informations, comme les opérations ou les projections disponibles sur l'instance. -On peut trouver un [exemple](../../docker/config/road2.json) de ce fichier et le [modèle](./configuration_model.yaml) au format YAML. +On peut trouver un [exemple](../../docker/config/service.json) de ce fichier et le [modèle](./service_model.yaml) au format YAML. ## log4js.json @@ -17,7 +37,7 @@ Le format est celui d'un JSON qui contient deux objets `mainConf` et `httpConf`. Le contenu de `mainConf` est un objet de configuration log4js. Le contenu de `httpConf` est un attribut `level` reprenant les niveaux proposés par log4js et un attribut `format` reprenant la syntaxe disponible pour log4js. Ces deux attributs doivent être présents. -On peut trouver un [exemple](../../docker/config/log4js.json) de ce fichier au format JSON. C'est celui qui est utilisé dans les images docker. +On peut trouver un [exemple](../../docker/config/log4js-service.json) de ce fichier au format JSON. C'est celui qui est utilisé dans les images docker. ## cors.json diff --git a/documentation/configuration/configuration_model.yaml b/documentation/configuration/service_model.yaml similarity index 88% rename from documentation/configuration/configuration_model.yaml rename to documentation/configuration/service_model.yaml index 37e0750..5edef64 100644 --- a/documentation/configuration/configuration_model.yaml +++ b/documentation/configuration/service_model.yaml @@ -1,4 +1,4 @@ -# Description d'un fichier de configuration de l'application Road4 +# Description d'un fichier de configuration de service # Une application est un objet qui contient toutes les informations utiles pour que l'application puisse diffuser ses services. "application": @@ -138,3 +138,18 @@ "directory": type: string required: true + # Indication des apis utilisées pour cette instance. Cela doit correspondre avec un dossier présent dans les APIs du projet + "apis": + required: true + minItems: 1 + items: + type: object + properties: + # Nom de l'api + "name": + type: string + required: true + # Version de l'api + "version": + type: string + required: true diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index d154121..ee3e3fe 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -112,7 +112,23 @@ Cette partie décrit l'application de ces concepts dans le code au cours d'une e ### 2.1 Au lancement de l'application -Road2 est un serveur web. Son point d'entrée est le fichier `src/js/road2.js`. Ce fichier va générer une instance de la classe `Service`. +Le projet Road2 propose deux serveurs web, un service et un administrateur. Il donc possède deux points d'entrée selon l'usage que l'on souhaite en faire. On peut lancer uniquement le service et cela fonctionnera très bien. Et on peut aussi lancer un administrateur uniquement. Celui-ci lancera un service quand on le lui demandera. Enfin, on peut lancer les deux d'un coup. + +#### 2.1.1 Lancement de l'administrateur + +Le premier point d'entrée possible est le fichier `src/js/road2.js`. Ce fichier va générer une instance de la classe `Administrator`. + +Cet administrateur permet plusieurs choses : +- On peut le lancer uniquement pour vérifier la bonne configuration de l'administrateur et des services associés. Dans ce cas là, le processus s'éteint après la vérification et renvoie un code d'erreur permettant de déterminer s'il y a eu un problème et son type. +- On peut le lancer en mode serveur pour administrer un ou plusieurs services via une API HTTP(S). Dans ce cas là, il est possible de lui demander de lancer les service à son démarrage. Il sera aussi possible de les démarrer plus tard. + +Un administrateur a été créé pour réaliser des tâches qui auraient gêné la bonne exécution du service. + +L'administrateur a donc été créé pour être indépendant du service. Si l'administrateur a des tâches fastidieuses, cela n'impacte pas le service. Si l'un tombe, l'autre non. + +#### 2.1.2 Lancement d'un service + +Le point d'entrée historique est le fichier `src/js/service/main.js`. Ce fichier va générer une instance de la classe `Service`. Ce service est l'objet qui permet de gérer les ressources proposées par l'instance en cours. Il contient donc un catalogue de ressources et un manager de ressources. @@ -122,7 +138,25 @@ Lorsque l'application est lancée, on commence par lire la configuration de l'ap Après cela, on charge les ressources et les sources du service indiquées dans la configuration. C'est à ce moment que les fichiers sont lus, stockés en RAM si nécessaire, et que les connexions aux bases de données sont effectuées. -Enfin, on finit par charger les APIs exposées par le service. C'est là qu'ExpressJS crée le ou les serveurs Node et charge les routes disponibles. +Enfin, on finit par charger les APIs exposées par le service. C'est là qu'ExpressJS crée le ou les serveurs Node et charge les routes disponibles. + +#### 2.1.3 Zoom sur la vérification de la configuration + +Que ce soit un administrateur ou un service, la configuration sera vérifiée. + +Cela passe généralement par des managers. + +##### Les managers + +La plupart des classes ont un manager. Ce manager permet comme précisé juste avant de vérifier les configurations. Mais il permet aussi de créer les instances des classes concernées. Enfin, il garde aussi un trace des différentes instances et permet donc de les gérer. + +Les managers sont conçus pour être utilisés de la manière suivante : on le crée sans configuration. Par contre, il peut avoir d'autres managers en paramètre. +Une fois créé, ce manager peut être utilisé pour vérifier une configuration. On peut lui donner la configuration d'un objet ou on peut parfois lui donner un ensemble de configuration. Dans ce deuxième cas, il y aura généralement une cohérence à vérifier entre chaque configuration. +Ensuite, on pourra charger des objets à partir de leur configuration. Il est important de faire un check avant un load. Car les load présupposent la validité de la configuration. Ce qui est connu par un check. De la même manière, on pourra parfois en charger plusieurs par un seul appel au manager. Le fait de charger une seule fois une configuration présente en divers endroits sera géré dans le manager. + +Pour bien fonctionner, le manager aura donc deux listes. Une liste plutôt éphémère qui gardera une trace des configurations déjà vérifiées. Elle servira à vérifier la cohérence de l'ensemble des configurations. Cette liste devra être vidée quand les vérifications seront terminées. +La deuxième liste sera une liste des configurations déjà chargées. Cette liste est persistante et indique l'état du manager. Elle sert à s'assurer que l'on charge une seule fois chaque configuration même si elle est demandée plusieurs fois. +Aussi, quand on souhaitera modifier la configuration durant la vie de l'application, c'est cette liste qui sera considérée la première pour vérifier la cohérence. La première liste ne sera réutilisée que si c'est un ensemble censé être cohérent que l'on vérifie. ### 2.2 : A la réception d'une requête diff --git a/documentation/developers/readme.md b/documentation/developers/readme.md index 5edb197..088204c 100644 --- a/documentation/developers/readme.md +++ b/documentation/developers/readme.md @@ -16,11 +16,17 @@ L'ensemble des fonctionnalités sont répertoriées à [part](./functionnalities ## Participer aux développements -Les participations à ce projet sont encouragées. L'ajout de moteurs ou d'API, bien évidemment. Mais toutes autres fonctionnalités sont les bienvenues. Nous avons mis en place une [documentation](./modification.md) afin de faciliter des développements. +Les participations à ce projet sont encouragées. L'ajout de moteurs ou d'API, bien évidemment. Mais toutes autres fonctionnalités sont les bienvenues. + +### Prise en main du projet + +Nous avons mis en place une [documentation](./modification.md) afin de faciliter la prise en main du projet. ### GIT -Comme cela aura certainement déjà été compris, ce projet utilise GIT. La gestion des branches et des versions pour les développements est détaillée [ici](./version.md). +Afin de pousser des développements sur le projet, ces derniers doivent être fournis par l'intermédiaire de `Pull Request` depuis votre branche vers la branche `develop` du projet. + +Plus généralement, la gestion des branches et des versions pour les développements est détaillée [ici](./version.md). ## Outils pour le développement diff --git a/package.json b/package.json index ef2427c..83c4c1b 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ "utest": "mocha --recursive './test/unit/mocha/**/*.js'", "itest": "mocha --recursive './test/integration/mocha/**/*.js'", "rtest": "HTTP_PROXY='' ./node_modules/cucumber/bin/cucumber-js ./test/functional/request/cucumber/features/req*.feature", - "ctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/configuration*.feature", + "ctest": "./node_modules/cucumber/bin/cucumber-js ./test/functional/configuration/cucumber/features/conf*.feature", "lint": "eslint -c eslint.json ./src/", "jsdoc": "jsdoc -c jsdoc.json", - "debug": "node --inspect=0.0.0.0:9229 ./src/js/road2.js" + "debug": "env NODE_ENV=debug node --inspect=0.0.0.0:9229 ./src/js/road2.js" }, "dependencies": { "@mapbox/polyline": "1.1.1", diff --git a/readme.md b/readme.md index 72c7f32..627a1c1 100644 --- a/readme.md +++ b/readme.md @@ -71,7 +71,7 @@ Afin que le serveur fonctionne, il est nécessaire de le [configurer](./document ### Lancement -Une fois configuré, il est possible de lancer le serveur avec la commande: +Une fois configuré, il est possible de lancer une instance de Road2 avec la commande: ``` node ${road2}/src/js/road2.js --ROAD2_CONF_FILE=${configuration}/server.json ``` diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js new file mode 100644 index 0000000..32ba3bd --- /dev/null +++ b/src/js/administrator/administrator.js @@ -0,0 +1,479 @@ +'use strict'; + +const express = require('express'); +const log4js = require('log4js'); +const helmet = require('helmet'); +const path = require('path'); +const fs = require('fs'); +const assert = require('assert').strict; +const serverManager = require('../server/serverManager'); +const serviceManager = require('../service/serviceManager'); +const apisManager = require('../apis/apisManager'); + +// Création du LOGGER +const LOGGER = log4js.getLogger("ADMINISTRATOR"); + +/** +* +* @class +* @name Administrator +* @description Pour chaque démarrage de Road2, il y a un Administrateur qui gère les services rendus. C'est un serveur qui écoute sur un port différent des serveurs des services. +* +*/ + +module.exports = class Administrator { + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe Administrator + * + */ + + constructor() { + + // Manager des serveurs, il contient la liste des serveurs diponibles + this._serverManager = new serverManager(); + + // APIs utilisées pour parler avec l'administrateur + this._apisManager = new apisManager(); + + // Manager des services gérés par l'administrateur + this._serviceManager = new serviceManager(); + + // Configuration de l'administration + this._configuration = {}; + + // Chemin absolu de la configuration + this._configurationPath = ""; + + // Configuration des logs + this._logConfiguration = {}; + + + } + + /** + * + * @function + * @name checkAdminConfiguration + * @description Vérifier la partie administration de la configuration + * @param {json} configuration - Configuration de Road2 (contenu du server.json) + * @param {string} configurationPath - Chemin de la configuration de Road2 (chemin du server.json) + * + */ + + checkAdminConfiguration(configuration, configurationPath) { + + LOGGER.info("Verification de la configuration de l'administrateur..."); + + // Fichier complet + if (!configuration) { + LOGGER.error("Configuration absente"); + return false; + } else { + LOGGER.debug("Configuration présente"); + } + + // Partie administration + if (!configuration.administration) { + LOGGER.error("Mauvaise configuration: 'administration' absent."); + return false; + } else { + LOGGER.debug("configuration.administration présent"); + } + + // Services à administrer + if (!configuration.administration.services) { + LOGGER.error("Mauvaise configuration: 'administration.services' absent."); + return false; + } else { + LOGGER.debug("configuration.administration.services présent"); + + if (!Array.isArray(configuration.administration.services)) { + LOGGER.error("Mauvaise configuration: 'administration.services' n'est pas un tableau."); + return false; + } else { + + LOGGER.debug("configuration.administration.services est bien un tableau"); + + if (configuration.administration.services.length === 0) { + LOGGER.error("Mauvaise configuration: 'administration.services' est un tableau vide."); + return false; + } else { + LOGGER.debug("configuration.administration.services est bien un tableau qui contient des éléments"); + } + + } + } + + // On ne veut pas que les Id soient réutilisés + let checkedServiceId = new Array(); + let checkedServiceConf = new Array(); + let checkedServiceConfLocation = new Array(); + + for (let i = 0; i < configuration.administration.services.length; i++) { + + let curServiceConf = configuration.administration.services[i]; + + let configurationLocation = ""; + let configurationContent = {}; + + if (!curServiceConf.id) { + LOGGER.error("Mauvaise configuration: 'id' absent."); + return false; + } else { + + if (typeof(curServiceConf.id) !== "string") { + LOGGER.error("Mauvaise configuration: 'id' n'est pas une string."); + return false; + } + if (curServiceConf.id === "") { + LOGGER.error("Mauvaise configuration: 'id' est une string vide."); + return false; + } + + LOGGER.debug("Id présent : " + curServiceConf.id); + + // On vérifie que l'id n'est pas déjà pris + if (checkedServiceId.length === 0) { + // On continue la suite de la vérification + } else { + + for (let j = 0; j < checkedServiceId.length; j++) { + if (curServiceConf.id === checkedServiceId[j]) { + LOGGER.error("Id de service déjà pris : " + curServiceConf.id); + return false; + } + } + + } + + } + + if (!curServiceConf.configuration) { + LOGGER.error("Mauvaise configuration: 'configuration' absent."); + return false; + } else { + + LOGGER.debug("configuration présent"); + + try { + configurationLocation = path.resolve(path.dirname(this._configurationPath), curServiceConf.configuration); + LOGGER.debug("Chemin absolu du fichier de configuration du service : " + configurationLocation); + } catch (error) { + LOGGER.error("Can't get absolute path of service configuration: " + curServiceConf.configuration); + LOGGER.error(error); + return false; + } + + // On vérifie que ce chemin n'est pas déjà utilisé + if (checkedServiceConfLocation.length !== 0) { + for (let cs = 0; cs < checkedServiceConfLocation.length; cs++) { + if (configurationLocation === checkedServiceConfLocation[cs]) { + LOGGER.error("La configuration indiquée est déjà vérifiée. Elle ne peut être utilisée pour un autre service géré par cet administrateur."); + return false; + } + } + } + + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + configurationContent = JSON.parse(fs.readFileSync(configurationLocation)); + LOGGER.debug("Le contenu du fichier est accessible par Road2"); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de configuration du service: " + configurationLocation); + LOGGER.error(error); + return false; + } + + // On vérifie que ce contenu n'est pas déjà utilisé + if (checkedServiceConf.length !== 0) { + for (let cs = 0; cs < checkedServiceConf.length; cs++) { + try { + assert.deepStrictEqual(configurationContent, checkedServiceConf[cs]); + LOGGER.error("Le contenu de configuration indiqué est déjà vérifié pour un autre service. Il ne peut être utilisée pour plus d'un service géré par cet administrateur."); + return false; + } catch (err) { + LOGGER.debug("Les deux configuration de service ne sont pas identiques."); + } + } + } + + + + } + + if (!curServiceConf.onStart) { + LOGGER.error("Mauvaise configuration: 'onStart' absent."); + return false; + } else { + + LOGGER.debug("configuration.onStart présent"); + + if (curServiceConf.onStart !== "true" && curServiceConf.onStart !== "false") { + LOGGER.error("Mauvaise configuration: 'onStart' doit être 'true' ou 'false'."); + return false; + } else { + LOGGER.debug("configuration.onStart bien configuré"); + } + + } + + if (!curServiceConf.creationType) { + LOGGER.error("Mauvaise configuration: 'creationType' absent."); + return false; + } else { + + LOGGER.debug("configuration.creationType présent"); + + if (!["sameProcess","newProcess","findByURI"].includes(curServiceConf.creationType)) { + LOGGER.error("Mauvaise configuration: 'creationType' doit être parmi 'sameProcess', 'newProcess', 'findByURI'."); + return false; + } else { + LOGGER.debug("configuration.creationType bien configuré"); + } + + } + + LOGGER.debug("Vérification du service en cours terminée"); + checkedServiceId.push(curServiceConf.id); + checkedServiceConf.push(configurationContent); + checkedServiceConfLocation.push(configurationLocation); + + + } + + LOGGER.debug("Vérification des services terminée"); + + // API utilisée pour administrer + if (!configuration.administration.api) { + LOGGER.fatal("Mauvaise configuration: 'administration.api' absent."); + return false; + } else { + LOGGER.debug("configuration.administration.api présent"); + } + + if (!this._apisManager.checkApiConfiguration(configuration.administration.api)) { + LOGGER.fatal("Mauvaise configuration: 'administration.api' mal configuré."); + return false; + } else { + LOGGER.debug("configuration.administration.api bien configuré"); + } + + // Partie network de l'administrateur + if (!configuration.administration.network) { + LOGGER.fatal("Mauvaise configuration: 'administration.network' absent."); + return false; + } else { + LOGGER.debug("configuration.administration.network présent"); + } + + if (!configuration.administration.network.server) { + LOGGER.fatal("Mauvaise configuration: 'administration.network.server' absent."); + return false; + } else { + LOGGER.debug("configuration.administration.network.server présent"); + } + + if (!this._serverManager.checkServerConfiguration(configuration.administration.network.server)){ + LOGGER.fatal("Mauvaise configuration: 'administration.network.server' mal configuré."); + return false; + } else { + LOGGER.debug("configuration.administration.network.server bien configuré"); + } + + LOGGER.info("La configuration de l'administrateur est correcte"); + return true; + + } + + /** + * + * @function + * @name saveAdminConfiguration + * @description Sauvegarder la partie administration de la configuration + * @param {json} configuration - Configuration de Road2 (contenu du server.json) + * @param {string} configurationPath - Chemin de la configuration de Road2 (chemin du server.json) + * @param {json} logConfiguration - Configuration des logs Road2 (contenu du log4js-administrator.json) + * + */ + + saveAdminConfiguration(configuration, configurationPath, logConfiguration) { + + LOGGER.info("Sauvegarde de la configuration de l'administrateur dans son instance..."); + + this._configuration = configuration; + this._configurationPath = configurationPath; + this._logConfiguration = logConfiguration; + + } + + /** + * + * @function + * @name createServer + * @description Création et démarrage du serveur pour l'administration + * + */ + + createServer() { + + LOGGER.info("Creation de l'application web Express..."); + + // Application Express + let administrator = express(); + + // Gestion des en-têtes avec helmet selon les préconisations d'ExpressJS + administrator.use(helmet()); + + // Pour le log des requêtes reçues sur le service avec la syntaxe + LOGGER.info("Instanciation du logger pour les requêtes..."); + + try { + + administrator.use(log4js.connectLogger(log4js.getLogger('request'), { + level: this._logConfiguration.httpConf.level, + format: (req, res, format) => format(this._logConfiguration.httpConf.format) + })); + + } catch (error) { + LOGGER.fatal("Impossible de connecter le logger pour les requetes: "); + LOGGER.error(this._logConfiguration.httpConf) + LOGGER.error(error); + return false; + } + + // Chargement de l'API + LOGGER.info("Chargement de l'API d'administration..."); + if (!this._apisManager.loadApiConfiguration(administrator, this._configuration.administration.api)) { + LOGGER.error("Erreur lors du chargement de l'API."); + return false; + } else { + LOGGER.debug("API chargée"); + } + + administrator.all('/', (req, res) => { + res.send('Road2 Administrator'); + }); + + // Création du serveur + LOGGER.info("Création du serveur d'administration..."); + if (!this._serverManager.loadServerConfiguration(administrator, this._configuration.administration.network.server)) { + LOGGER.fatal("Impossible de creer le serveur d'administration."); + return false; + } else { + LOGGER.debug("Serveur d'administration créé"); + } + + // Démarrage du serveur + LOGGER.info("Démarrage du serveur d'administration..."); + if (!this._serverManager.startAllServers()) { + LOGGER.fatal("Impossible de démarrer le serveur d'administration."); + return false; + } else { + LOGGER.debug("Serveur d'administration démarré"); + } + + return true; + + } + + /** + * + * @function + * @name checkServicesConfiguration + * @description Vérification de la configuration des services indiqués dans server.json + * + */ + + async checkServicesConfiguration() { + + LOGGER.info("Vérification de la configuration des services..."); + + for (let i = 0; i < this._configuration.administration.services.length; i++) { + + let curIASConf = this._configuration.administration.services[i]; + LOGGER.debug("Vérification du service : " + curIASConf.id); + + // Récupération de la configuration + let file = path.resolve(path.dirname(this._configurationPath), curIASConf.configuration); + let serviceConfiguration = JSON.parse(fs.readFileSync(file)); + + // Vérification + if (!(await this._serviceManager.checkServiceConfiguration(serviceConfiguration, file))) { + LOGGER.error("La configuration du service "+ curIASConf.id +" est incorrecte"); + return false; + } else { + LOGGER.info("La configuration du service "+ curIASConf.id +" est correcte"); + } + + + } + + return true; + + } + + /** + * + * @function + * @name createServicesOnStart + * @description Création des services gérés par cet administrateur dont la création a été demandé au démarrage de l'administrateur + * + */ + + async createServicesOnStart() { + + LOGGER.info("Vérification et création des services onStart=true..."); + + // Pour chaque service, on va voir s'il doit être démarré + // Si oui, on vérifie sa configuration puis on le démarre + + for (let i = 0; i < this._configuration.administration.services.length; i++) { + + let curIASConf = this._configuration.administration.services[i]; + LOGGER.debug("Analyse du service : " + curIASConf.id); + + if (curIASConf.onStart === "false") { + // il n'y a rien à faire + LOGGER.debug("Le service " + curIASConf.id + " n'est à démarrer tout de suite"); + continue; + } else { + LOGGER.info("Le service " + curIASConf.id + " est à démarrer"); + } + + // Récupération de la configuration + LOGGER.info("Récupération de la configuration du service"); + let serviceConfLocation = path.resolve(path.dirname(this._configurationPath), curIASConf.configuration); + let serviceConfiguration = JSON.parse(fs.readFileSync(serviceConfLocation)); + + // Vérification de la configuration + LOGGER.info("Vérification de la configuration..."); + if (!(await this._serviceManager.checkServiceConfiguration(serviceConfiguration, serviceConfLocation))) { + LOGGER.error("La configuration du service "+ curIASConf.id +" est incorrecte"); + return false; + } else { + LOGGER.info("La configuration du service "+ curIASConf.id +" est correcte"); + } + + // Chargement du service + LOGGER.info("Chargement du service..."); + if (!this._serviceManager.loadService(curIASConf.creationType, curIASConf.id, serviceConfLocation, serviceConfiguration)) { + LOGGER.error("Impossible de créer le service "+ curIASConf.id); + // On essaye les suivants + continue; + } else { + LOGGER.info("Le service " + curIASConf.id + " a été lancé correctement"); + } + + } + + return true; + + } + +} + + diff --git a/src/js/apis/apisManager.js b/src/js/apis/apisManager.js index df9c68a..5cbf799 100644 --- a/src/js/apis/apisManager.js +++ b/src/js/apis/apisManager.js @@ -19,7 +19,13 @@ module.exports = class apisManager { */ constructor() { - // Liste des routes chargées par app + // Dossier contenant les APIs du projet + this._apisDirectory = "../apis/"; + + // Prefix utilisé pour chaque route des APIS + this._prefix = ""; + + // Liste des routes chargées par l'app Express this._listOfRoutes = new Array(); // Catalogue des apis @@ -27,6 +33,28 @@ module.exports = class apisManager { } + /** + * + * @function + * @name get apisDirectory + * @description Récupérer le chemin du dossier qui contient les APIs + * + */ + get apisDirectory () { + return this._apisDirectory; + } + + /** + * + * @function + * @name get prefix + * @description Récupérer le prefix utilisé par ce manager + * + */ + get prefix () { + return this._prefix; + } + /** * * @function @@ -54,31 +82,119 @@ module.exports = class apisManager { * @function * @name getApi * @description Récupérer l'api qui correspond à l'id et la version demandée - * @param {string} id - Id de l'api concerné (simple) + * @param {string} name - Nom de l'api concerné (simple) * @param {string} version - Version de l'api concerné (1.0.0) * */ - getApi (id, version) { - return this._apisCatalog[id+version]; + getApi (name, version) { + return this._apisCatalog[name+version]; + } + + /** + * + * @function + * @name checkApiConfiguration + * @description Vérifier la configuration d'une API + * @param {object} configuration - Configuration de l'API à vérifier + * @return {boolean} + * + */ + checkApiConfiguration (configuration) { + + // Nom de l'API + if (!configuration.name) { + + LOGGER.error("Mauvaise configuration de l'api: 'name' absent."); + return false; + + } else { + + if (typeof configuration.name !== "string") { + LOGGER.error("Mauvaise configuration: 'name' n'est pas une string."); + return false; + } else { + + // On vérifie que le dossier correspondant existe + let tmpPathName = path.resolve(__dirname, this._apisDirectory) + "/" + configuration.name; + let availableAPI = false; + try { + availableAPI= fs.statSync(tmpPathName).isDirectory() + } catch(error) { + LOGGER.error("Mauvaise configuration: 'name' ne peut être évalué."); + return false; + } + + if (!availableAPI) { + LOGGER.error("Mauvaise configuration de l'api: 'name' n'est pas disponible."); + return false; + } else { + + // Version de l'API + if (!configuration.version) { + LOGGER.error("Mauvaise configuration de l'api: 'version' absent."); + return false; + } else { + + if (typeof configuration.name !== "string") { + LOGGER.error("Mauvaise configuration de l'api: 'name' n'est pas une string."); + return false; + } else { + + // On vérifie que le dossier correspondant existe + let tmpPathVersion = tmpPathName + "/" + configuration.version; + let availableVersion = false; + + try { + availableVersion= fs.statSync(tmpPathVersion).isDirectory() + } catch(error) { + LOGGER.error("Mauvaise configuration de l'api: 'version' ne peut être évalué."); + return false; + } + + if (!availableVersion) { + LOGGER.error("Mauvaise configuration de l'api: 'version' n'est pas disponible."); + return false; + } else { + + // On vérifie qu'il y a au moins un index.js + let tmpIndex = tmpPathVersion + "/index.js"; + try { + fs.accessSync(tmpIndex, fs.constants.R_OK); + return true; + } catch (err) { + LOGGER.error("Le fichier " + tmpIndex + " ne peut etre lu."); + return false; + } + + } + + } + + } + + } + + } + + } + } /** * * @function - * @name loadAPISDirectory + * @name loadApiDirectory * @description Fonction utilisée pour charger l'ensemble des APIs disponibles dans un dossier. * @param {express} app - Objet créé par ExpressJS représentant l'application - * @param {string} dir - Dossier contenant les routers à charger dans app - * @param {string} prefix - Préfixe utilisé pour chaque router ajouté * */ - loadAPISDirectory (app, dir, prefix) { + loadApiDirectory (app) { LOGGER.info("Chargement des APIS..."); - // Soit dir est un chemin absolu qui pointe vers un autre repo d'APIs, soit c'est celui du repo officiel de Road2 (usage de __dirname) - let APIsDirectory = path.resolve(__dirname, dir); + // Soit _apisDirectory est un chemin absolu qui pointe vers un autre repo d'APIs, soit c'est celui du repo officiel de Road2 (usage de __dirname) + let APIsDirectory = path.resolve(__dirname, this._apisDirectory); // on lit le contenu du dossier let APIsDirectoryTable = new Array(); @@ -126,86 +242,119 @@ module.exports = class apisManager { // c'est un dossier qui contient potentiellement une version de l'API LOGGER.info("Nouvelle version: " + apiVersion); - let APIFile = APIDirectoryVersion + "/index.js"; - //on cherche le fichier index.js qui contient la description de l'API - if (fs.statSync(APIFile).isFile()) { - - // Gestion du router - // ------------------ - let route; - - if (prefix !== "") { - route = "/" + prefix + "/" + apiName + "/" + apiVersion; - } else { - route = "/"+ apiName + "/" + apiVersion; - } - // On vérifie que la route n'existe pas déjà - if (this.verifyRouteExistanceById(route)) { - // Si elle existe c'est un vrai problème donc on arrête le chargement - LOGGER.error("La route " + route + " existe deja. L'api n'est donc pas chargee."); - return false; - } + let configuration = { + name: apiName, + version: apiVersion + }; - // on crée un objet Api qui va permettre de suivre l'évolution de l'api au cours de la vie du service - let api = new Api(apiName, apiVersion, APIFile); + if (!this.loadApiConfiguration(app, configuration)) { + LOGGER.error("Impossible de charger l'API " + apiName + "/" + apiVersion); + } - // Gestion de l'initialisation de l'api - // ---------------------- - let initFile = APIDirectoryVersion + "/init.js"; - try { + } else { + // Si ce n'est pas un dossier, on ne fait rien + } - fs.statSync(initFile); - api.initFile = initFile; + } - } catch(err) { - // Ce n'est pas obligatoire, on ne fait donc rien - } + } else { + // Si ce n'est pas un dossier, on ne fait rien + } - // ---------------------- + } - // Gestion de l'update de l'api - // ---------------------- - let updateFile = APIDirectoryVersion + "/update.js"; - try { + LOGGER.info("APIS chargees."); + return true; - fs.statSync(updateFile); - api.updateFile = updateFile; + } - } catch(err) { - // Ce n'est pas obligatoire, on ne fait donc rien - } - // ---------------------- + /** + * + * @function + * @name loadApiConfiguration + * @description Fonction utilisée pour charger l'API d'un seul dossier + * @param {express} app - Objet créé par ExpressJS représentant l'application + * @param {object} configuration - Configuration d'une API + * + */ - if (!api.initialize(route, app)) { - LOGGER.error("L'initialisation de l'api s'est mal deroulee."); - return false; - } + loadApiConfiguration (app, configuration) { - // on stocke l'objet Api dans l'apiManager - this._apisCatalog[apiName+apiVersion] = api; - // on stocke la route - this._listOfRoutes.push(route); + let apiName = configuration.name; + let apiVersion = configuration.version; - } else { - // le fichier index.js n'existe pas donc on ne fait rien - LOGGER.error("Pas de fichier index.js dans le dossier " + APIDirectoryVersion); - } + // Soit _apisDirectory est un chemin absolu qui pointe vers un autre repo d'APIs, soit c'est celui du repo officiel de Road2 (usage de __dirname) + let APIDirectory = path.resolve(__dirname, this._apisDirectory) + "/" + apiName + "/" + apiVersion; - } else { - // Si ce n'est pas un dossier, on ne fait rien - } + let APIFile = APIDirectory + "/index.js"; + //on cherche le fichier index.js qui contient la description de l'API + if (fs.statSync(APIFile).isFile()) { - } + // Gestion du router + // ------------------ + let route; + if (this._prefix !== "") { + route = "/" + this._prefix + "/" + apiName + "/" + apiVersion; } else { - // Si ce n'est pas un dossier, on ne fait rien + route = "/"+ apiName + "/" + apiVersion; } - } + // On vérifie que la route n'existe pas déjà + if (this.verifyRouteExistanceById(route)) { + // Si elle existe c'est un vrai problème donc on arrête le chargement + LOGGER.error("La route " + route + " existe deja. L'api n'est donc pas chargee."); + return false; + } - LOGGER.info("APIS chargees."); - return true; + // on crée un objet Api qui va permettre de suivre l'évolution de l'api au cours de la vie du service + let api = new Api(apiName, apiVersion, APIFile); + + // Gestion de l'initialisation de l'api + // ---------------------- + let initFile = APIDirectory + "/init.js"; + try { + + fs.statSync(initFile); + api.initFile = initFile; + + } catch(err) { + // Ce n'est pas obligatoire, on ne fait donc rien + } + + // ---------------------- + + // Gestion de l'update de l'api + // ---------------------- + let updateFile = APIDirectory + "/update.js"; + try { + + fs.statSync(updateFile); + api.updateFile = updateFile; + + } catch(err) { + // Ce n'est pas obligatoire, on ne fait donc rien + } + // ---------------------- + + if (!api.initialize(route, app)) { + LOGGER.error("L'initialisation de l'api s'est mal deroulee."); + return false; + } + + // on stocke l'objet Api dans l'apisManager + this._apisCatalog[apiName+apiVersion] = api; + // on stocke la route + this._listOfRoutes.push(route); + + return true; + + } else { + // le fichier index.js n'existe pas donc on ne fait rien + LOGGER.error("Pas de fichier index.js dans le dossier " + APIDirectory); + return false; + } } diff --git a/src/js/apis/simple/1.0.0/init.js b/src/js/apis/simple/1.0.0/init.js index 63eb3f2..1ce8980 100644 --- a/src/js/apis/simple/1.0.0/init.js +++ b/src/js/apis/simple/1.0.0/init.js @@ -588,7 +588,7 @@ module.exports = { // --- resources getCapabilities.resources = new Array(); - let resources = service.resourceCatalog; + let resources = service.getResources(); for(let resourceId in resources) { diff --git a/src/js/base/base.js b/src/js/base/base.js index e4231f8..b20e576 100644 --- a/src/js/base/base.js +++ b/src/js/base/base.js @@ -86,11 +86,15 @@ module.exports = class Base { throw errorManager.createError("PG is not available"); } + // TODO : supprimer le return si pas utile + // return new Promise(); + } catch (err) { LOGGER.error("connection error", err.stack) throw errorManager.createError("Cannot connect to database"); } + } /** diff --git a/src/js/base/baseManager.js b/src/js/base/baseManager.js index d6c20cd..0866f49 100644 --- a/src/js/base/baseManager.js +++ b/src/js/base/baseManager.js @@ -1,7 +1,6 @@ 'use strict'; const log4js = require('log4js'); -const path = require('path'); const fs = require('fs'); const Base = require('./base'); @@ -26,8 +25,12 @@ module.exports = class baseManager { */ constructor() { - // Liste des descriptions de bases vérifiées et validées par le manager - this._listOfVerifiedDbConfig = new Array(); + // Liste des descriptions de bases déjà chargées par le manager + this._loadedBaseConfiguration = new Array(); + + // Liste des descriptions de bases déjà vérifiées par le manager et qui doivent être cohérentes avec les prochaines qui seront vérifiés + // Cet objet doit être vidé quand l'ensemble des configurations a été vérifié + this._checkedBaseConfiguration = new Array(); // Le catalogue des bases créées par le manager this._baseCatalog = {}; @@ -37,12 +40,12 @@ module.exports = class baseManager { /** * * @function - * @name get listOfVerifiedDbConfig - * @description Récupérer la liste des configurations + * @name get loadedBaseConfiguration + * @description Récupérer la liste des configurations déjà chargées par ce manager * */ - get listOfVerifiedDbConfig() { - return this._listOfVerifiedDbConfig; + get loadedBaseConfiguration() { + return this._loadedBaseConfiguration; } /** @@ -59,20 +62,45 @@ module.exports = class baseManager { /** * * @function - * @name checkBase + * @name getBase + * @description Récupérer une base par sa configuration + * @param {string} configPath - Chemin de la configuration + * + */ + getBase(configPath) { + return this._baseCatalog[configPath]; + } + + /** + * + * @function + * @name checkBaseConfiguration * @description Vérification de la description de la configuration à une base de données. * @param{string} dbConfigPath - Nom du fichier contenant les informations de connexion à la base (chemin absolu) * */ - async checkBase(dbConfigPath) { + async checkBaseConfiguration(dbConfigPath) { LOGGER.info("Verification de la configuration de la base de donnees..."); - if (this._listOfVerifiedDbConfig.length !== 0) { + if (this._loadedBaseConfiguration.length !== 0) { + + for (let i = 0; i < this._loadedBaseConfiguration.length; i++) { + if (dbConfigPath === this._loadedBaseConfiguration[i]) { + LOGGER.info("Configuration deja chargée.") + return true; + } + } - for (let i = 0; i < this._listOfVerifiedDbConfig.length; i++) { - if (dbConfigPath === this._listOfVerifiedDbConfig[i]) { - LOGGER.info("Configuration deja verifiee.") + } else { + // C'est le premier, donc on continue + } + + if (this._checkedBaseConfiguration.length !== 0) { + + for (let i = 0; i < this._checkedBaseConfiguration.length; i++) { + if (dbConfigPath === this._checkedBaseConfiguration[i]) { + LOGGER.info("Configuration deja vérifiée.") return true; } } @@ -105,7 +133,7 @@ module.exports = class baseManager { return false; } - const base = new Base(configuration); + let base = new Base(configuration); try { LOGGER.info("Test de connexion à la base de donnees..."); @@ -116,9 +144,6 @@ module.exports = class baseManager { return false; } - // on stocke le chemin du fichier - this._listOfVerifiedDbConfig.push(dbConfigPath); - LOGGER.info("Configuration de la base de donnees valide."); return true; @@ -127,20 +152,49 @@ module.exports = class baseManager { /** * * @function - * @name createBase - * @description Création de la connexion à une base de données + * @name flushCheckedBaseConfiguration + * @description Vider la liste des configurations de bases déjà vérifiées + * + */ + flushCheckedBaseConfiguration() { + + this._checkedBaseConfiguration = new Array(); + + } + + /** + * + * @function + * @name saveCheckedBaseConfiguration + * @description Sauvegarde de la description de la configuration à une base de données. + * @param {string} dbConfigPath - Nom du fichier contenant les informations de connexion à la base (chemin absolu) + * + */ + saveCheckedBaseConfiguration(dbConfigPath) { + + this._checkedBaseConfiguration.push(dbConfigPath); + + } + + /** + * + * @function + * @name loadBaseConfiguration + * @description Création de la base de données * @param{string} dbConfigPath - Nom du fichier contenant les informations de connexion à la base (chemin absolu) * */ - createBase(dbConfigPath) { + loadBaseConfiguration(dbConfigPath) { let base; // on vérifie d'abord que la base n'a pas déjà été créée if (this._baseCatalog[dbConfigPath]) { - return this._baseCatalog[dbConfigPath]; + return true; } else { - // la base n'existe pas, on continue + // TODO la base n'existe pas, on vérifie que le contenu de la conf n'est pas le même qu'une base déjà chargée. + // Cela permet d'éviter de créer des connexions inutiles. + // Si elle existe déjà, il faut bien veiller à renvoyer le this._baseCatalog[dbConfigPath] correspondant. } let configuration = {}; @@ -155,8 +209,9 @@ module.exports = class baseManager { base = new Base(configuration); this._baseCatalog[dbConfigPath] = base; + this._loadedBaseConfiguration = dbConfigPath; - return base; + return true; } diff --git a/src/js/geography/projectionManager.js b/src/js/geography/projectionManager.js index 1b89530..fb1d755 100644 --- a/src/js/geography/projectionManager.js +++ b/src/js/geography/projectionManager.js @@ -1,7 +1,6 @@ 'use strict'; const fs = require('fs'); -const path = require('path'); const proj4 = require('proj4'); const log4js = require('log4js'); @@ -28,40 +27,72 @@ module.exports = class ProjectionManager { */ constructor() { - // Liste des ids de projections disponibles - this._listOfProjectionId = new Array(); + // Liste des ids de projections disponibles car déjà chargées dans proj4 + this._loadedProjectionId = new Array(); + + // Liste des ids des projections déjà vérifiées et qui doivent être cohérentes avec les prochaines qui seront vérifiés + // Cet objet doit être vidé quand l'ensemble des configurations a été vérifié + this._checkedProjectionId = new Array(); } /** * * @function - * @name get listOfProjectionId + * @name get loadedProjectionId * @description Récupérer la liste des ids de projections disponibles * */ - get listOfProjectionId () { - return this._listOfProjectionId; + get loadedProjectionId () { + return this._loadedProjectionId; + } + + /** + * + * @function + * @name isAvailable + * @description Savoir si une projection est disponible dans l'instance de proj4 + * @param {string} id - ID de la projection + * + */ + isAvailable (id) { + + if (!id) { + return false; + } + if (this._loadedProjectionId.length === 0) { + return false; + } + + for (let i = 0; i < this._loadedProjectionId.length; i++) { + if (id === this._loadedProjectionId[i]) { + return true; + } + } + + return false; + } /** * * @function - * @name isAvailableById - * @description Savoir si une projection est disponible + * @name isChecked + * @description Savoir si une projection a été validé durant l'étape de vérification + * @param {string} id - ID de la projection * */ - isAvailableById (id) { + isChecked (id) { if (!id) { return false; } - if (this._listOfProjectionId.length === 0) { + if (this._checkedProjectionId.length === 0) { return false; } - for (let i = 0; i < this._listOfProjectionId.length; i++) { - if (id === this._listOfProjectionId[i]) { + for (let i = 0; i < this._checkedProjectionId.length; i++) { + if (id === this._checkedProjectionId[i]) { return true; } } @@ -70,6 +101,190 @@ module.exports = class ProjectionManager { } + /** + * + * @function + * @name checkProjectionDirectory + * @description Vérifier les projections d'un dossier + * @param {string} projectionDirectory - Chemin absolu d'un dossier de projections + * + */ + checkProjectionDirectory(projectionDirectory) { + + LOGGER.info("Vérification du dossier de projections..."); + + if (fs.existsSync(projectionDirectory)) { + + try { + + // On vérifie que l'application peut lire les fichiers du dossier + if (!fs.readdirSync(projectionDirectory).every(projectionName => { + + let projectionFile = projectionDirectory + "/" + projectionName; + + if (!this.checkProjectionFile(projectionFile)) { + LOGGER.error("Le fichier " + projectionFile + " est mal configuré"); + return false; + } else { + LOGGER.info("Le fichier " + projectionFile + " est bien configuré"); + return true; + } + + }) + ) { + LOGGER.error("Un des fichiers de projection est mal configuré"); + return false; + } + + } catch(error) { + LOGGER.error("Impossible de lire le dossier"); + return false; + } + + return true; + + } else { + LOGGER.fatal("Mauvaise configuration: Dossier de projections inexistant : " + projectionDirectory); + return false; + } + + } + + + /** + * + * @function + * @name checkProjectionFile + * @description Vérifier les projections d'un fichier + * @param {string} projectionFile - Chemin absolu d'un fichier de projections + * + */ + checkProjectionFile(projectionFile) { + + LOGGER.info("Vérification du fichier de projections : " + projectionFile); + + try { + fs.accessSync(projectionFile, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier de projection ne peut etre lu: " + projectionFile); + return false; + } + + let fileContent = {}; + try { + fileContent = JSON.parse(fs.readFileSync(projectionFile)); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de projection: " + projectionFile); + LOGGER.error(error); + return false; + } + + if (!fileContent.projectionsList) { + LOGGER.error("La fichier des projections ne contient pas de liste"); + return false; + } + + if (!Array.isArray(fileContent.projectionsList)) { + LOGGER.error("L'attribut projectionsList de la configuration n'est pas un tableau."); + return false; + } + + if (fileContent.projectionsList.length === 0) { + LOGGER.error("L'attribut projectionsList de la configuration est un tableau vide."); + return false; + } + + for (let i = 0; i < fileContent.projectionsList.length; i++) { + if (!this.checkProjectionConfiguration(fileContent.projectionsList[i])) { + LOGGER.error("Mauvaise configuration d'une projection dans le fichier."); + return false; + } else { + this._checkedProjectionId.push(fileContent.projectionsList[i].id); + } + } + + return true; + + } + + /** + * + * @function + * @name checkProjectionConfiguration + * @description Vérifier les projections d'un fichier + * @param {object} configuration - Configuration d'une projection + * + */ + checkProjectionConfiguration(configuration) { + + LOGGER.info("Vérification d'une projection"); + + // id de la projection + if (!configuration) { + LOGGER.error("La configuration de la projection est vide"); + return false; + } + + // id de la projection + if (!configuration.id) { + LOGGER.error("La configuration de la projection n'a pas d'id"); + return false; + } else { + LOGGER.debug("Projection id : " + configuration.id); + } + + // Parametres de la projection + if (!configuration.parameters) { + LOGGER.error("La configuration de la projection n'a pas de parametres"); + return false; + } else { + + if (typeof configuration.parameters !== "string") { + LOGGER.error("Les parametres de la projection ne sont pas une string"); + return false; + } else { + LOGGER.debug("Projection parameters : " + configuration.parameters); + } + + } + + // On vérifie que l'id n'est pas déjà chargé + if (this._loadedProjectionId.length !== 0) { + for (let i = 0; i < this._loadedProjectionId.length; i++) { + if (configuration.id === this._loadedProjectionId[i]) { + LOGGER.error("Une projection contenant l'id " + configuration.id + " est deja chargee."); + return false; + } + } + } + + // On vérifie que l'id n'est pas déjà pris par le check courant + if (this._checkedProjectionId.length !== 0) { + for (let i = 0; i < this._checkedProjectionId.length; i++) { + if (configuration.id === this._checkedProjectionId[i]) { + LOGGER.error("Une projection contenant l'id " + configuration.id + " est deja verifiee."); + return false; + } + } + } + + return true; + + } + + /** + * + * @function + * @name flushCheckedProjection + * @description Vider la liste des projections déjà vérifiées + * + */ + flushCheckedProjection() { + + this._checkedProjectionId = new Array(); + + } + /** * * @function @@ -137,7 +352,7 @@ module.exports = class ProjectionManager { LOGGER.error("Pas de fichier"); return false; } else { - LOGGER.info(file); + LOGGER.debug(file); } let pathFile = file; @@ -174,7 +389,7 @@ module.exports = class ProjectionManager { } for (let i = 0; i < fileContent.projectionsList.length; i++) { - if (!this.loadProjection(fileContent.projectionsList[i])) { + if (!this.loadProjectionConfiguration(fileContent.projectionsList[i])) { LOGGER.error("Erreur lors du chargement d'une projection du fichier."); return false; } @@ -188,12 +403,12 @@ module.exports = class ProjectionManager { /** * * @function - * @name loadProjection + * @name loadProjectionConfiguration * @description Charger la projection décrite via sa configuration * @param {json} configuration - configuration de la projection * */ - loadProjection (configuration) { + loadProjectionConfiguration (configuration) { LOGGER.info("Chargement d'une projection"); @@ -208,7 +423,7 @@ module.exports = class ProjectionManager { LOGGER.error("La configuration de la projection n'a pas d'id"); return false; } else { - LOGGER.info(configuration.id); + LOGGER.debug(configuration.id); } // Parametres de la projection @@ -216,13 +431,14 @@ module.exports = class ProjectionManager { LOGGER.error("La configuration de la projection n'a pas de parametres"); return false; } else { - // TODO: vérification des parametres ? + // On ne vérifie pas les paramètres car c'est fait dans le check + LOGGER.debug(configuration.parameters); } // On vérifie que l'id n'est pas déjà pris - if (this._listOfProjectionId.length !== 0) { - for (let i = 0; i < this._listOfProjectionId.length; i++) { - if (configuration.id === this._listOfProjectionId[i]) { + if (this._loadedProjectionId.length !== 0) { + for (let i = 0; i < this._loadedProjectionId.length; i++) { + if (configuration.id === this._loadedProjectionId[i]) { LOGGER.error("Une projection contenant l'id " + configuration.id + " est deja referencee."); return false; } @@ -234,7 +450,7 @@ module.exports = class ProjectionManager { proj4.defs(configuration.id, configuration.parameters); // On stocke l'id - this._listOfProjectionId.push(configuration.id); + this._loadedProjectionId.push(configuration.id); } catch(error) { LOGGER.error("Impossible de charger la projection dans proj4: "); diff --git a/src/js/operations/operationManager.js b/src/js/operations/operationManager.js index 2ba7873..919f1aa 100644 --- a/src/js/operations/operationManager.js +++ b/src/js/operations/operationManager.js @@ -1,7 +1,6 @@ 'use strict'; const fs = require('fs'); -const path = require('path'); const log4js = require('log4js'); const ParameterManager = require('../parameters/parameterManager'); const Operation = require('../operations/operation'); @@ -30,8 +29,11 @@ module.exports = class operationManager { */ constructor() { + // Liste des ids appartenant aux opérations chargées par le manager + this._loadedOperationId = new Array(); + // Liste des ids appartenant aux opérations vérifiées par le manager - this._listOfVerifiedOperationId = new Array(); + this._checkedOperationId = new Array(); // Parameter manager this._parameterManager = new ParameterManager(); @@ -40,19 +42,27 @@ module.exports = class operationManager { this._operationCatalog = {}; // Catalogue des configurations des opérations disponibles - this._operationConfigurationCatalog = {}; + this._checkedOperationConfiguration = {}; } /** * * @function - * @name get listOfVerifiedOperationId - * @description Récupérer l'ensemble des ids appartenant aux opérations vérifiées par le manager + * @name verifyCheckedOperation + * @description Savoir si une opération est vérifiée sur le service + * @param {string} operationId - Id de l'opération * */ - get listOfVerifiedOperationId() { - return this._listOfVerifiedOperationId; + verifyCheckedOperation(operationId) { + + for (let i = 0; i < this._checkedOperationId.length; i++) { + if (this._checkedOperationId[i] === operationId) { + return true; + } + } + return false; + } /** @@ -90,87 +100,72 @@ module.exports = class operationManager { /** * * @function - * @name loadOperationDirectory - * @description Charger les opérations du dossier - * @param {string} userOperationDirectory - Dossier contenant les opérations (chemin absolu) - * @param {string} userParameterDirectory - Dossier contenant les paramètres (chemin absolu) - * @return {boolean} + * @name get checkOperationDirectory + * @description Vérifier les configurations d'opération d'un dossier + * @param {string} directory - Dossier qui contient les configurations d'opération à vérifier * */ - loadOperationDirectory(userOperationDirectory, userParameterDirectory) { - - LOGGER.info("Chargement des operations..."); - - // Vérification de l'existence du dossier operations - let operationsDirectory = userOperationDirectory; - if (!fs.statSync(operationsDirectory).isDirectory()) { - LOGGER.error("Le dossier contenant la configuration des operations n'existe pas: " + operationsDirectory); - return false; - } + checkOperationDirectory(directory) { - // Vérification de l'existence du dossier parameters - let parametersDirectory = userParameterDirectory; - if (!fs.statSync(parametersDirectory).isDirectory()) { - LOGGER.error("Le dossier contenant la configuration des parametres n'existe pas: " + parametersDirectory); - return false; - } + LOGGER.info("Vérification du dossier d'opération..."); - // Chargement des paramètres - // C'est le parameterManager qui s'en charge - if (!this._parameterManager.loadParameterDirectory(parametersDirectory)) { - LOGGER.error("Erreur lors du chargement des parametres"); + if (!fs.existsSync(directory)) { + LOGGER.error("Le dossier" + directory + "n'existe pas"); return false; - } else { - LOGGER.info("Chargement des parametres ok"); } - // Chargement des opérations - fs.readdirSync(operationsDirectory).forEach(operationConfFileName => { - - let operationConfFile = operationsDirectory + "/" + operationConfFileName; + // On vérifie que l'application peut lire les fichiers du dossier + if (!fs.readdirSync(directory).every(operation => { - if (fs.statSync(operationConfFile).isFile()) { - - // on récupère le contenu du fichier puis on le vérifie - let operationConf = JSON.parse(fs.readFileSync(operationConfFile)); + let operationFile = ""; - if (this.checkOperationConf(operationConf)) { - // on stocke l'id si tout va bien - this._listOfVerifiedOperationId.push(operationConf.id); - // on crée l'opération et on l'ajoute au catalogue - // pour créer l'opération, il faut d'abord créer les paramètres - let parametersTable = this._parameterManager.createParameters(operationConf); - this._operationConfigurationCatalog[operationConf.id] = operationConf; - this._operationCatalog[operationConf.id] = new Operation(operationConf.id, operationConf.name, operationConf.description, parametersTable); + try { + operationFile = directory + "/" + operation; + fs.accessSync(operationFile, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier d'operation ne peut etre lu: " + operationFile); + } - } else { - LOGGER.error("La configuration d'une operation est incorrecte: " + operationConfFile); - return false; - } + let fileContent = {}; + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + fileContent = JSON.parse(fs.readFileSync(operationFile)); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier d'operation: " + operationFile); + LOGGER.error(error); + return false; + } + if (!this.checkOperationConfiguration(fileContent)) { + LOGGER.error("Operation mal configurée"); + return false; } else { - // On ne fait rien + this._checkedOperationId.push(fileContent.id); + this._checkedOperationConfiguration[fileContent.id] = fileContent; + LOGGER.info("Operation bien configurée"); + return true; } - }); - - LOGGER.info("Operations chargees."); + }) + ) { + LOGGER.error("Une des opérations est mal configurée"); + return false; + } return true; - + } - /** * * @function - * @name checkOperationConf + * @name checkOperationConfiguration * @description Vérifier la configuration d'une opération * @param {json} operationConf - Configuration d'une opération de service * @return {boolean} * */ - checkOperationConf(operationConf) { + checkOperationConfiguration(operationConf) { LOGGER.info("Verification de l'operation"); @@ -180,12 +175,28 @@ module.exports = class operationManager { return false; } else { - // On vérifie que l'id n'est pas déjà pris. - if (this._listOfVerifiedOperationId.length !== 0) { + // On vérifie que l'id n'est pas déjà chargé. + if (this._loadedOperationId.length !== 0) { + + for (let i = 0; i < this._loadedOperationId.length; i++ ) { + if (this._loadedOperationId[i] === operationConf.id) { + LOGGER.info("L'operation contenant l'id " + operationConf.id + " est deja chargée."); + return false; + } else { + // on continue de vérifier + } + } + + } else { + // C'est la première operation. On ne fait rien, on continue la vérification + } + + // On vérifie que l'id n'est pas déjà vérifié. + if (this._checkedOperationId.length !== 0) { - for (let i = 0; i < this._listOfVerifiedOperationId.length; i++ ) { - if (this._listOfVerifiedOperationId[i] === operationConf.id) { - LOGGER.info("L'operation contenant l'id " + operationConf.id + " est deja referencee."); + for (let i = 0; i < this._checkedOperationId.length; i++ ) { + if (this._checkedOperationId[i] === operationConf.id) { + LOGGER.info("L'operation contenant l'id " + operationConf.id + " est deja vérifié."); return false; } else { // on continue de vérifier @@ -224,7 +235,7 @@ module.exports = class operationManager { if (operationConf.parameters.length !== 0) { // on vérifie la validité des ids de paramètre fournis for (let i = 0; i < operationConf.parameters.length; i++ ) { - if (!this._parameterManager.isParameterAvailable(operationConf.parameters[i])) { + if (!this._parameterManager.isParameterChecked(operationConf.parameters[i])) { LOGGER.error("L'operation précise un parametre qui n'est pas disponible: " + operationConf.parameters[i]); return false; } else { @@ -245,15 +256,150 @@ module.exports = class operationManager { /** * * @function - * @name checkResourceOperationConf + * @name flushCheckedOperation + * @description Vider la liste des opérations et paramètres déjà vérifiés + * + */ + flushCheckedOperation() { + + this._checkedOperationId = new Array(); + this._checkedOperationConfiguration = {}; + this._parameterManager.flushCheckedParameter(); + + } + + /** + * + * @function + * @name checkParameterDirectory + * @description Vérifier les configurations de paramètre d'un dossier + * @param {string} directory - Dossier qui contient les configurations de paramètre à vérifier + * + */ + checkParameterDirectory(directory) { + + return this._parameterManager.checkParameterDirectory(directory); + + } + + /** + * + * @function + * @name loadParameterDirectory + * @description Charger les configurations de paramètre d'un dossier + * @param {string} directory - Dossier qui contient les configurations de paramètre à charger + * + */ + loadParameterDirectory(directory) { + + return this._parameterManager.loadParameterDirectory(directory); + + } + + /** + * + * @function + * @name loadOperationDirectory + * @description Charger les opérations du dossier + * @param {string} operationsDirectory - Dossier contenant les opérations (chemin absolu) + * @return {boolean} + * + */ + loadOperationDirectory(operationsDirectory) { + + LOGGER.info("Chargement des operations d'un dossier..."); + + // Vérification de l'existence du dossier operations + if (!fs.statSync(operationsDirectory).isDirectory()) { + LOGGER.error("Le dossier contenant la configuration des operations n'existe pas: " + operationsDirectory); + return false; + } + + // Chargement des opérations + if (!fs.readdirSync(operationsDirectory).every(operationConfFileName => { + + let operationConfFile = operationsDirectory + "/" + operationConfFileName; + + if (fs.statSync(operationConfFile).isFile()) { + + // on récupère le contenu du fichier puis on le vérifie + let operationConf = {}; + try { + operationConf = JSON.parse(fs.readFileSync(operationConfFile)); + } catch(error) { + LOGGER.error("Impossible de lire la configuration de l'opération dans le fichier " + operationConfFile); + return false; + } + + if (!this.loadOperationConfiguration(operationConf)) { + LOGGER.error("Impossible de charger l'opération configurée dans le fichier " + operationConfFile); + return false; + } + + } else { + // On ne fait rien + } + + return true; + + }) + ) { + LOGGER.error("Une des opérations n'a pas pu être chargée"); + return false; + } + + LOGGER.info("Operations chargees."); + + return true; + + } + + /** + * + * @function + * @name loadOperationConfiguration + * @description Charger une opération à partir de sa configuration + * @param {object} operationConf - Configuration de l'opération + * @return {boolean} + * + */ + loadOperationConfiguration(operationConf) { + + // on crée l'opération et on l'ajoute au catalogue + // pour créer l'opération, il faut d'abord récupérer les paramètres + let parametersTable = this._parameterManager.getParameters(operationConf); + + if (parametersTable === null) { + LOGGER.error("Impossible de récuperer l'ensemble des parametres pour créer l'opération"); + return false; + } else { + if (parametersTable.length === 0) { + LOGGER.error("Impossible de récuperer les parametres pour créer l'opération"); + return false; + } else { + this._operationCatalog[operationConf.id] = new Operation(operationConf.id, operationConf.name, operationConf.description, parametersTable); + } + } + + // on stocke l'id si tout va bien + this._loadedOperationId.push(operationConf.id); + + return true; + + } + + /** + * + * @function + * @name checkResourceOperationConfiguration * @description Vérifier la configuration d'une opération de ressource * @param {json} resourceOperationJsonObject - Configuration d'une opération de ressource * @return {boolean} * */ - checkResourceOperationConf(resourceOperationJsonObject) { + checkResourceOperationConfiguration(resourceOperationJsonObject) { - LOGGER.info("Verification de l'operation de la ressource"); + LOGGER.info("Verification de l'operation de la ressource..."); // on regarde d'abord la taille du tableau donné en entrée if (resourceOperationJsonObject.length === 0) { @@ -273,7 +419,7 @@ module.exports = class operationManager { LOGGER.info(currentOperationConf.id); // on vérifie qu'elle est bien disponible pour cette instance du service - if (!this.verifyAvailabilityOperation(currentOperationConf.id)) { + if (!this.verifyCheckedOperation(currentOperationConf.id)) { LOGGER.error("L'operation indiquee n'est pas disponible"); // TODO: remplacer ce return par un continue pour affiner le chargement des ressources // par exemple, si la ressource indique une opération non disponible mais qu'on veuille quand même la charger pour les opérations disponibles @@ -301,7 +447,7 @@ module.exports = class operationManager { // On prend la liste des paramètres censé être là et on vérifie qu'ils sont bien présents et valides // On compare le nombre de paramètres attendus au nombre de paramètres présents et on compare pour être certain qu'il n'y en ait pas trop - let wantedParameters = this._operationConfigurationCatalog[currentOperationConf.id].parameters; + let wantedParameters = this._checkedOperationConfiguration[currentOperationConf.id].parameters; if (wantedParameters.length !== currentOperationConf.parameters.length) { LOGGER.error("Le nombre de parametres presents n'est pas celui attendu"); @@ -311,7 +457,7 @@ module.exports = class operationManager { for(let j = 0; j < currentOperationConf.parameters.length; j++) { let currentParameterConf = currentOperationConf.parameters[j]; - if (!this._parameterManager.checkResourceParameterConf(currentParameterConf)) { + if (!this._parameterManager.checkResourceParameterConfiguration(currentParameterConf)) { LOGGER.error("L'objet representant un parametre est mal configure"); return false; } else { @@ -319,55 +465,7 @@ module.exports = class operationManager { } } - } - - } - - } - - return true; - - } - - /** - * - * @function - * @name getResourceOperationConf - * @description Récupérer la liste des opérations disponibles sur une ressource - * @param {json} resourceOperationJsonObject - Configuration d'une opération de ressource - * @param {table} operationTable - Tableau contenant les ids d'opérations de ressource - * @return {boolean} - * - */ - getResourceOperationConf(resourceOperationJsonObject, operationTable) { - - LOGGER.info("Recuperation des operations de la ressource"); - - // on regarde d'abord la taille du tableau donné en entrée - if (resourceOperationJsonObject.length === 0) { - LOGGER.error("Il n'y aucune operation decrite"); - return false; - } else { - - // on vérifie les opérations unes à une - for (let i = 0; i < resourceOperationJsonObject.length; i++) { - let currentOperationConf = resourceOperationJsonObject[i]; - - if (!currentOperationConf.id) { - LOGGER.error("L'objet representant l'operation n'a pas d'id"); - return false; - } else { - - LOGGER.info(currentOperationConf.id); - - // on vérifie qu'elle est bien disponible pour cette instance du service - if (!this.verifyAvailabilityOperation(currentOperationConf.id)) { - LOGGER.error("L'operation indiquee n'est pas disponible"); - return false; - } else { - // on le stocke - operationTable.push(currentOperationConf.id); - } + LOGGER.info("Operation de ressource bien configurée"); } @@ -382,53 +480,19 @@ module.exports = class operationManager { /** * * @function - * @name isAvailableInTable - * @description Savoir si une opération est disponible dans une liste d'opérations de ressource - * @param {string} operationId - Id de l'opération de ressource recherchée - * @param {table} operationTable - Tableau contenant les ids d'opérations de ressource - * @return {boolean} - * - */ - - isAvailableInTable (operationId, resourceOperationTable) { - - if (resourceOperationTable.length === 0) { - LOGGER.error("Le tableau d'operations est vide.") - return false; - } else { - - for (let i = 0; i < resourceOperationTable.length; i++) { - LOGGER.info(resourceOperationTable[i]); - if (operationId === resourceOperationTable[i]) { - return true; - } - } - - } - - LOGGER.info("Operation non trouvee."); - return false; - } - - /** - * - * @function - * @name createResourceOperation + * @name loadResourceOperationConfiguration * @description Créer l'ensemble des opérations d'une ressource * @param {object} resourceOperationHash - Objet contenant les opérations de ressource * @param {json} resourceJsonObject - Configuration d'une opération de ressource * @return {boolean} * */ - createResourceOperation(resourceOperationHash, resourceJsonObject) { + loadResourceOperationConfiguration(resourceOperationHash, resourceJsonObject) { LOGGER.info("Creation des operations de la ressource"); // on crée les opérations unes à une for (let i = 0; i < resourceJsonObject.resource.availableOperations.length; i++) { - - // TODO: tester à nouveau si l'opération est bien disponible sur le service - // cela permet de ne charger que les opérations disponibles // on isole la conf de l'opération let currentOperationConf = resourceJsonObject.resource.availableOperations[i]; @@ -437,7 +501,7 @@ module.exports = class operationManager { // création des paramètres de l'opération de ressource let resourceParameterHash = {}; - if (!this._parameterManager.createResourceParameter(resourceParameterHash, currentOperationConf)) { + if (!this._parameterManager.loadResourceParameterConfiguration(resourceParameterHash, currentOperationConf)) { LOGGER.error("Erreur lors de la creation des parametres de l'operation"); return false; } diff --git a/src/js/parameters/parameterManager.js b/src/js/parameters/parameterManager.js index 02c8def..11bf941 100644 --- a/src/js/parameters/parameterManager.js +++ b/src/js/parameters/parameterManager.js @@ -33,12 +33,18 @@ module.exports = class parameterManager { */ constructor() { + // Liste des ids appartenant aux paramètres chargés par le manager + this._loadedParameterId = new Array(); + // Liste des ids appartenant aux paramètres vérifiés par le manager - this._listOfVerifiedParameterId = new Array(); + this._checkedParameterId = new Array(); // Stockage des configurations des paramètres de service this._parametersConfiguration = {}; + // Stockage des configurations des paramètres de service vérifiés par le manager + this._checkedParametersConfiguration = {}; + // Stockage des paramètres de service this._parameters = {}; @@ -47,55 +53,51 @@ module.exports = class parameterManager { /** * * @function - * @name get listOfVerifiedParameterId + * @name get loadedParameterId * @description Récupérer l'ensemble des ids appartenant aux paramètres vérifiés par le manager * */ - get listOfVerifiedParameterId() { - return this._listOfVerifiedParameterId; - } - - /** - * - * @function - * @name get parametersConfiguration - * @description Récupérer l'ensemble des configurations appartenant aux paramètres vérifiés par le manager - * - */ - get parametersConfiguration() { - return this._parametersConfiguration; + get loadedParameterId() { + return this._loadedParameterId; } /** * * @function - * @name getParameterConfigurationById - * @description Récupérer la configuration d'un paramètre via son id + * @name isParameterLoaded + * @description Permet de savoir si un paramètre est disponible, via son id * @param {string} id - Id du paramètre de service - * @return {object} Instance de Parameter, paramètre de service + * @return {boolean} * */ - getParameterConfigurationById(id) { - if (this._parametersConfiguration[id]) { - return this._parametersConfiguration[id]; + isParameterLoaded(id) { + if (this._loadedParameterId.length !== 0) { + for (let i=0; i < this._loadedParameterId.length; i++) { + if (id === this._loadedParameterId[i]) { + return true; + } else { + // on continue + } + } } else { - return {}; + return false; } + return false; } /** * * @function - * @name isParameterAvailable - * @description Permet de savoir si un paramètre est disponible, via son id + * @name isParameterChecked + * @description Permet de savoir si un paramètre a été vérifié, via son id * @param {string} id - Id du paramètre de service * @return {boolean} * */ - isParameterAvailable(id) { - if (this._listOfVerifiedParameterId.length !== 0) { - for (let i=0; i < this._listOfVerifiedParameterId.length; i++) { - if (id === this._listOfVerifiedParameterId[i]) { + isParameterChecked(id) { + if (this._checkedParameterId.length !== 0) { + for (let i=0; i < this._checkedParameterId.length; i++) { + if (id === this._checkedParameterId[i]) { return true; } else { // on continue @@ -110,53 +112,55 @@ module.exports = class parameterManager { /** * * @function - * @name loadParameterDirectory - * @description Charger les paramètres du dossier - * @param {string} parametersDirectory - Dossier contenant la description des paramètres - * @return {boolean} + * @name get checkParameterDirectory + * @description Vérifier les configurations de paramètre d'un dossier + * @param {string} directory - Dossier qui contient les configurations de paramètre à vérifier * */ - loadParameterDirectory(parametersDirectory) { - - LOGGER.info("Chargement des parametres..."); + checkParameterDirectory(directory) { - // Vérification de l'existence du dossier /dir/parameters - if (!fs.statSync(parametersDirectory).isDirectory()) { - LOGGER.error("Le dossier contenant la configuration des parametres n'existe pas: " + parametersDirectory); + if (!fs.existsSync(directory)) { + LOGGER.fatal("Mauvaise configuration: Le dossier des parametres n'existe pas : " + directory); return false; - } - - // Chargement des paramètres - // On les charge en premier pour qu'au moment du chargement des opérations, on connaisse déjà les identifiants des paramètres - fs.readdirSync(parametersDirectory).forEach(parameterConfFileName => { - - let parameterConfFile = parametersDirectory + "/" + parameterConfFileName; - - if (fs.statSync(parameterConfFile).isFile()) { + } - LOGGER.info("Chargement du parametre depuis " + parameterConfFile); + // On vérifie que l'application peut lire les fichiers du dossier + if (!fs.readdirSync(directory).every(parameter => { - // on récupère le contenu du fichier puis on le vérifie et enfin on stocke l'id si tout va bien - let parameterConf = JSON.parse(fs.readFileSync(parameterConfFile)); + let parameterFile = ""; - if (this.checkParameterConf(parameterConf)) { - - this._listOfVerifiedParameterId.push(parameterConf.id); - this._parametersConfiguration[parameterConf.id] = parameterConf; + try { + parameterFile = directory + "/" + parameter; + fs.accessSync(parameterFile, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier de parametres ne peut etre lu: " + parameterFile); + } - } else { - // s'il y a une erreur dans le fichier on arrête - LOGGER.error("La configuration d'un parametres est incorrecte: " + parameterConfFile); - return false; - } + let contentFile = {}; + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + contentFile = JSON.parse(fs.readFileSync(parameterFile)); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de parametres: " + parameterFile); + LOGGER.error(error); + return false; + } + if (!this.checkParameterConfiguration(contentFile)) { + LOGGER.error("Parametre mal configuré"); + return false; } else { - // On ne fait rien + this._checkedParameterId.push(contentFile.id); + this._checkedParametersConfiguration[contentFile.id] = contentFile; + LOGGER.info("Parametre bien configurée"); + return true; } - }); - - LOGGER.info("Parametres charges."); + }) + ) { + LOGGER.error("Un des paramètres est mal configuré"); + return false; + } return true; @@ -165,13 +169,13 @@ module.exports = class parameterManager { /** * * @function - * @name checkParameterConf + * @name checkParameterConfiguration * @description Vérifier la configuration d'un paramètre * @param {json} parametersConf - Configuration du paramètre de service * @return {boolean} * */ - checkParameterConf(parameterConf) { + checkParameterConfiguration(parameterConf) { let tmpMin; let tmpMax; @@ -184,12 +188,28 @@ module.exports = class parameterManager { return false; } else { LOGGER.info(parameterConf.id); - // On vérifie que l'id n'est pas déjà pris. - if (this._listOfVerifiedParameterId.length !== 0) { + // On vérifie que l'id n'est pas déjà chargé. + if (this._loadedParameterId.length !== 0) { + + for (let i = 0; i < this._loadedParameterId.length; i++ ) { + if (this._loadedParameterId[i] === parameterConf.id) { + LOGGER.info("Le parametre contenant l'id " + parameterConf.id + " est deja chargé."); + return false; + } else { + // on continue de vérifier + } + } + + } else { + // C'est le premier paramètre. On ne fait rien, on continue la vérification + } + + // On vérifie que l'id n'est pas déjà vérifié. + if (this._checkedParameterId.length !== 0) { - for (let i = 0; i < this._listOfVerifiedParameterId.length; i++ ) { - if (this._listOfVerifiedParameterId[i] === parameterConf.id) { - LOGGER.info("Le parametre contenant l'id " + parameterConf.id + " est deja reference."); + for (let i = 0; i < this._checkedParameterId.length; i++ ) { + if (this._checkedParameterId[i] === parameterConf.id) { + LOGGER.info("Le parametre contenant l'id " + parameterConf.id + " est deja vérifié."); return false; } else { // on continue de vérifier @@ -340,45 +360,157 @@ module.exports = class parameterManager { /** * * @function - * @name createParameters - * @description Créer les objets paramètres à partir de la configuration d'une opération - * @param {json} operationConf - Configuration d'une opération de service - * @return {table} Tableau d'instance de Parameter, paramètre de service + * @name flushCheckedParameter + * @description Vider la liste des paramètres déjà vérifiés * */ - createParameters(operationConf) { + flushCheckedParameter() { - let parametersTable = {}; + this._checkedParameterId = new Array(); + this._checkedParametersConfiguration = {}; + + } - for (let i = 0; i < operationConf.parameters.length; i++ ) { + /** + * + * @function + * @name loadParameterDirectory + * @description Charger les paramètres du dossier + * @param {string} parametersDirectory - Dossier contenant la description des paramètres + * @return {boolean} + * + */ + loadParameterDirectory(parametersDirectory) { - let parameterId = operationConf.parameters[i]; - let parameterConf = this._parametersConfiguration[parameterId]; + LOGGER.info("Chargement des parametres du dossier..."); - parametersTable[parameterId] = new Parameter(parameterId, parameterConf.type, parameterConf.name, parameterConf.description, parameterConf.required, parameterConf.defaultValue); + // Vérification de l'existence du dossier /dir/parameters + if (!fs.statSync(parametersDirectory).isDirectory()) { + LOGGER.error("Le dossier contenant la configuration des parametres n'existe pas: " + parametersDirectory); + return false; + } - if (parameterConf.example) { - parametersTable[parameterId].example = parameterConf.example; - } + // Chargement des paramètres + // On les charge en premier pour qu'au moment du chargement des opérations, on connaisse déjà les identifiants des paramètres + if (!fs.readdirSync(parametersDirectory).every(parameterConfFileName => { - if (parameterConf.min) { - parametersTable[parameterId].min = parameterConf.min; - } + let parameterConfFile = parametersDirectory + "/" + parameterConfFileName; - if (parameterConf.max) { - parametersTable[parameterId].max = parameterConf.max; - } + if (fs.statSync(parameterConfFile).isFile()) { - if (parameterConf.explode) { - parametersTable[parameterId].explode = parameterConf.explode; - } + LOGGER.info("Chargement du parametre depuis " + parameterConfFile); + + // on récupère le contenu du fichier puis on le vérifie et enfin on stocke l'id si tout va bien + let parameterConf = {}; + try { + parameterConf = JSON.parse(fs.readFileSync(parameterConfFile)); + } catch(error) { + LOGGER.error("Impossible de lire " + parameterConfFile); + LOGGER.error(error); + return false; + } + + if (!this.loadParameterConfiguration(parameterConf)) { + LOGGER.error("Impossible de charger le parametre"); + return false; + } - if (parameterConf.style) { - parametersTable[parameterId].style = parameterConf.style; + } else { + // On ne fait rien } - // Stocakge du paramètre - this._parameters[parameterId] = parametersTable[parameterId]; + return true; + + }) + ) { + LOGGER.error("Un des paramètres n'a pas pu être chargé"); + return false; + } + + LOGGER.info("Parametres charges."); + + return true; + + } + + /** + * + * @function + * @name loadParameterConfiguration + * @description Créer un paramètre à partir de sa configuration + * @param {json} configuration - Configuration d'un parametre + * @return {boolean} + * + */ + loadParameterConfiguration(configuration) { + + LOGGER.info("Chargement d'un parametre à partir de sa configuration..."); + + let parameterId = configuration.id; + let parameterConf = configuration; + + if (this._parameters[parameterId]) { + LOGGER.info("Le parametre existe déjà"); + return true; + } + + let parameter = new Parameter(parameterId, parameterConf.type, parameterConf.name, parameterConf.description, parameterConf.required, parameterConf.defaultValue); + + if (parameterConf.example) { + parameter.example = parameterConf.example; + } + + if (parameterConf.min) { + parameter.min = parameterConf.min; + } + + if (parameterConf.max) { + parameter.max = parameterConf.max; + } + + if (parameterConf.explode) { + parameter.explode = parameterConf.explode; + } + + if (parameterConf.style) { + parameter.style = parameterConf.style; + } + + // Stocakge du paramètre + this._parameters[parameterId] = parameter; + this._loadedParameterId.push(parameterId); + this._parametersConfiguration[parameterId] = parameterConf; + + return true; + + } + + /** + * + * @function + * @name getParameters + * @description Créer les objets paramètres à partir de la configuration d'une opération + * @param {json} operationConf - Configuration d'une opération de service + * @return {table} Tableau d'instance de Parameter, paramètre de service + * + */ + getParameters(operationConf) { + + LOGGER.info("Récupération des parametres d'une operation..."); + + let parametersTable = {}; + + for (let i = 0; i < operationConf.parameters.length; i++ ) { + + let parameterId = operationConf.parameters[i]; + + if (this._parameters[parameterId]) { + parametersTable[parameterId] = this._parameters[parameterId]; + LOGGER.debug("Parametre " + parameterId + " récupéré"); + } else { + LOGGER.error("Le parametre " + parameterId + " n'existe pas"); + return null; + } } @@ -389,13 +521,13 @@ module.exports = class parameterManager { /** * * @function - * @name checkResourceParameterConf + * @name checkResourceParameterConfiguration * @description Vérifier la configuration d'un paramètre de ressource * @param {json} resourceParameterJsonObject - Configuration d'un paramètre de ressource * @return {boolean} * */ - checkResourceParameterConf(resourceParameterJsonObject) { + checkResourceParameterConfiguration(resourceParameterJsonObject) { LOGGER.info("Verification du parametre de la ressource"); @@ -407,7 +539,7 @@ module.exports = class parameterManager { } // on vérifie qu'il est bien disponible pour cette instance du service - if (!this.isParameterAvailable(resourceParameterJsonObject.id)) { + if (!this.isParameterChecked(resourceParameterJsonObject.id)) { LOGGER.error("Le parametre indique n'est pas disponible"); return false; } else { @@ -415,7 +547,7 @@ module.exports = class parameterManager { } // on récupère la configuration du paramètre - let serviceParameterConf = this._parametersConfiguration[resourceParameterJsonObject.id]; + let serviceParameterConf = this._checkedParametersConfiguration[resourceParameterJsonObject.id]; if (!serviceParameterConf) { LOGGER.fatal("La configuration du parametre de service est introuvable !"); return false; @@ -776,14 +908,14 @@ module.exports = class parameterManager { /** * * @function - * @name createResourceParameter + * @name loadResourceParameterConfiguration * @description Créer l'ensemble des opérations d'une ressource * @param {object} resourceParameterHash - Objet contenant l'ensemble des paramètres de ressource pour une ressource * @param {json} currentOperationConf - Configuraton de l'opération de ressource pour une ressource * @return {boolean} * */ - createResourceParameter(resourceParameterHash, currentOperationConf) { + loadResourceParameterConfiguration(resourceParameterHash, currentOperationConf) { LOGGER.info("Creation des parametres de l'operation"); diff --git a/src/js/resources/osrmResource.js b/src/js/resources/osrmResource.js index 5298a5d..d202ea5 100644 --- a/src/js/resources/osrmResource.js +++ b/src/js/resources/osrmResource.js @@ -140,30 +140,4 @@ module.exports = class osrmResource extends Resource { } - /** - * - * @function - * @name removeSource - * @description Supprimer les references à une source au sein de la ressource - * @param {string} sourceId - Id de la source - * @return {boolean} - * - */ - removeSource (sourceId) { - - let keysToDelete = new Array(); - for (let key in this._linkedSource) { - if (this._linkedSource[key] === sourceId) { - keysToDelete.push(key); - } - } - if(keysToDelete.length !== 0) { - for (let i = 0; i < keysToDelete.length; i++) { - delete this._linkedSource[keysToDelete[i]]; - } - } - - return true; - } - } diff --git a/src/js/resources/pgrResource.js b/src/js/resources/pgrResource.js index 86aa58e..1a26e1b 100644 --- a/src/js/resources/pgrResource.js +++ b/src/js/resources/pgrResource.js @@ -127,30 +127,4 @@ module.exports = class pgrResource extends Resource { } - /** - * - * @function - * @name removeSource - * @description Supprimer les references à une source au sein de la ressource - * @param {string} sourceId - Id de la source - * @return {boolean} - * - */ - removeSource (sourceId) { - - let keysToDelete = new Array(); - for (const key in this._linkedSource) { - if (this._linkedSource[key] === sourceId) { - keysToDelete.push(key); - } - } - if(keysToDelete.length !== 0) { - for (let i = 0; i < keysToDelete.length; i++) { - delete this._linkedSource[keysToDelete[i]]; - } - } - - return true; - } - } diff --git a/src/js/resources/resource.js b/src/js/resources/resource.js index 3439dbb..354ae65 100644 --- a/src/js/resources/resource.js +++ b/src/js/resources/resource.js @@ -98,22 +98,6 @@ module.exports = class Resource { return sourceId; } - /** - * - * @function - * @name removeSource - * @description Supprimer les references à une source au sein de la ressource - * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. - * Dans la classe actuelle, ce n'est que pour indiquer qu'il faut implémenter la fonction - * dans chacune des classes filles. - * @param {string} sourceId - Id de la source - * @return {boolean} - * - */ - removeSource (sourceId) { - return true; - } - /** * * @function diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index 24a0e1d..6a20a39 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -1,5 +1,7 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); const osrmResource = require('../resources/osrmResource'); const pgrResource = require('../resources/pgrResource'); const smartpgrResource = require('../resources/smartpgrResource'); @@ -17,74 +19,128 @@ module.exports = class resourceManager { * @description Constructeur de la classe resourceManager * */ - constructor() { + constructor(sourceManager, operationManager, topologyManager) { + + // Liste des ids des ressources chargées par le manager + this._loadedResourceId = new Array(); // Liste des ids des ressources vérifiées par le manager - this._listOfVerifiedResourceIds = new Array(); + this._checkedResourceId = new Array(); + + // Liste des ressources chargées dans le manager + this._resource = {}; + + // Liste des types de ressource gérées par le manager + this._availableResourceTypes = ["pgr", "smartpgr","osrm"]; + + // Manager de topology + this._topologyManager = topologyManager; + + // Manager de source + this._sourceManager = sourceManager; - // Liste des ids des ressources gérées par le manager - this._listOfResourceIds = new Array(); + // Manager d'opération + this._operationManager = operationManager; } /** * * @function - * @name get listOfResourceIds - * @description Récupérer l'ensemble des ids de ressources + * @name get resource + * @description Récupérer les ressources * */ - get listOfResourceIds() { - return this._listOfResourceIds; + get resource() { + return this._resource; } /** * * @function - * @name removeResource - * @description Supprimer une ressource - * @param {string} id - Id de la ressource - * @return {boolean} + * @name checkResourceDirectory + * @description Fonction utilisée pour vérifier le contenu d'un dossier de description d'une ressource. + * @param {string} directory - Dossier qui contient les configurations des ressources + * @return {boolean} * */ - removeResource(id) { - let index = this._listOfVerifiedResourceIds.indexOf(id); - if (index !== -1) { - this._listOfVerifiedResourceIds.splice(index,1); - } else { - return false; - } - this._listOfVerifiedResourceIds.splice(index,1); + async checkResourceDirectory(directory) { + + LOGGER.info("Vérification d'un dossier de ressources..."); + LOGGER.info("Nom du dossier: " + directory); + + if (fs.existsSync(directory)) { + + let fileList = new Array(); + try { + fileList = fs.readdirSync(directory); + } catch(error) { + LOGGER.error("Impossible de lire le dossier :"); + LOGGER.error(error); + return false; + } + + if (fileList.length === 0) { + LOGGER.warn("Le dossier " + directory + " est vide"); + return false; + } + + for (let i = 0; i < fileList.length; i++) { + + let resource = fileList[i]; + let resourceFile = ""; + try { + resourceFile = directory + "/" + resource; + fs.accessSync(resourceFile, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier de ressource ne peut etre lu: " + resourceFile); + } + + let resourceConf = {}; + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + resourceConf = JSON.parse(fs.readFileSync(resourceFile)); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de ressource: " + resourceFile); + LOGGER.error(error); + return false; + } + + if (!(await this.checkResourceConfiguration(resourceConf))) { + LOGGER.error("La ressource décrite dans le fichier " + resourceFile + " est mal configuée"); + return false; + } else { + this._checkedResourceId.push(resourceConf.resource.id); + } + + } + + LOGGER.info("Vérification du dossier de ressources terminée"); + return true; - index = this._listOfResourceIds.indexOf(id); - if (index !== -1) { - this._listOfResourceIds.splice(index,1); } else { + LOGGER.error("Mauvaise configuration: Le dossier n'existe pas: " + directory ); return false; } - this._listOfResourceIds.splice(index,1); - return true; } /** * * @function - * @name checkResource + * @name checkResourceConfiguration * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une ressource. - * @param {json} resourceJsonObject - Description JSON de la ressource - * @param {object} sourceManager - Manager de source du service - * @param {object} operationManager - Manager d'opération du service - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @param {object} resourceJsonObject - Configuration de la ressource + * @return {boolean} * */ - async checkResource(resourceJsonObject, sourceManager, operationManager, topologyManager) { + async checkResourceConfiguration(resourceJsonObject) { - LOGGER.info("Verification de la ressource..."); + LOGGER.info("Verification de la configuration d'une ressource..."); - if (!resourceJsonObject.resource.id) { + if (!resourceJsonObject.resource) { LOGGER.error("Le fichier ne contient pas d'objet resource"); return false; } @@ -95,17 +151,31 @@ module.exports = class resourceManager { return false; } else { LOGGER.info("Ressource id: " + resourceJsonObject.resource.id); - // On vérifie que l'id de la ressource n'est pas déjà pris par une autre ressource. - if (this._listOfVerifiedResourceIds.length !== 0) { - for (let i = 0; i < this._listOfVerifiedResourceIds.length; i++ ) { - if (this._listOfVerifiedResourceIds[i] === resourceJsonObject.resource.id) { - LOGGER.error("Une ressource contenant l'id " + resourceJsonObject.resource.id + " a deja ete verifiee. Cette ressource ne peut donc etre ajoutee."); + + // On vérifie que l'id de la ressource n'est pas déjà pris par une autre ressource chargée + if (this._loadedResourceId.length !== 0) { + for (let i = 0; i < this._loadedResourceId.length; i++ ) { + if (this._loadedResourceId[i] === resourceJsonObject.resource.id) { + LOGGER.error("Une ressource contenant l'id " + resourceJsonObject.resource.id + " a deja ete chargée."); return false; } } } else { - // C'est la première ressource. + // Il n'y a pas encore de ressource chargée. } + + // On vérifie que l'id de la ressource n'est pas déjà pris par une autre ressource vérifiée + if (this._checkedResourceId.length !== 0) { + for (let i = 0; i < this._checkedResourceId.length; i++ ) { + if (this._checkedResourceId[i] === resourceJsonObject.resource.id) { + LOGGER.error("Une ressource contenant l'id " + resourceJsonObject.resource.id + " a deja ete verifiee."); + return false; + } + } + } else { + // C'est la première ressource vérifiée. + } + } // Version @@ -125,74 +195,33 @@ module.exports = class resourceManager { LOGGER.error("La ressource ne contient pas de type."); return false; } else { - // Vérification que le type est valide puis vérification spécifique à chaque type - let available = false; - // La partie délimitée peut être copié-collée pour ajouter un nouveau type. - // Il ne reste plus qu'à créer la fonction de vérification correspondante. - //------ OSRM - if (resourceJsonObject.resource.type === "osrm") { - available = true; - LOGGER.info("Ressource osrm."); - if (!this.checkResourceOsrm(resourceJsonObject.resource)) { - LOGGER.error("Erreur lors de la verification de la ressource osrm."); - return false; - } else { - // il n'y a eu aucun problème, la ressource est correctement configurée. - } - } else { - // On va voir si c'est un autre type. - } - //------ OSRM - //------ PGR - let pgrStyleResources = ["pgr", "smartpgr"]; - if (pgrStyleResources.includes(resourceJsonObject.resource.type)) { - available = true; - LOGGER.info("Ressource pgrouting."); - if (!this.checkResourcePgr(resourceJsonObject.resource)) { - LOGGER.error("Erreur lors de la verification de la ressource pgr."); - return false; - } else { - // il n'y a eu aucun problème, la ressource est correctement configurée. - } - } else { - // On va voir si c'est un autre type. - } - //------ PGR - // Si ce n'est aucun type valide, on renvoie une erreur. - if (!available) { + // Vérification que le type est valide + if (this._availableResourceTypes.includes(resourceJsonObject.resource.type)) { + LOGGER.info("Type de la ressource disponible: " + resourceJsonObject.resource.type); + } else { LOGGER.error("La ressource indique un type invalide: " + resourceJsonObject.resource.type); - return false; + return false; } + } + // Description + if (!resourceJsonObject.resource.description) { + LOGGER.error("La ressource ne contient pas de description."); + return false; + } + // Topology if (!resourceJsonObject.resource.topology) { LOGGER.error("La ressource ne contient pas de topologie."); return false; } else { - if (!(await topologyManager.checkTopology(resourceJsonObject.resource.topology))) { + if (!(await this._topologyManager.checkTopologyConfiguration(resourceJsonObject.resource.topology))) { LOGGER.error("La ressource contient une topologie incorrecte."); return false; - } - } - - let currentAvailableOp = new Array(); - // availableOperations - if (!resourceJsonObject.resource.availableOperations) { - LOGGER.error("La ressource ne contient pas de availableOperations."); - return false; - } else { - // on fait la vérification via le operationManager - if (!operationManager.checkResourceOperationConf(resourceJsonObject.resource.availableOperations)) { - LOGGER.error("Mauvaise configuration des operations dans la ressource."); - return false; } else { - // on récupère la liste des opérations validées pour cette ressource - if (!operationManager.getResourceOperationConf(resourceJsonObject.resource.availableOperations, currentAvailableOp)) { - LOGGER.error("Impossible de recuperer les operations de la ressource."); - return false; - } + this._topologyManager.saveCheckedTopology(resourceJsonObject.resource.topology); } } @@ -207,146 +236,181 @@ module.exports = class resourceManager { for (let i = 0; i < resourceJsonObject.resource.sources.length; i++ ) { let sourceJsonObject = resourceJsonObject.resource.sources[i]; - if (!sourceManager.checkSource(sourceJsonObject, operationManager, currentAvailableOp)) { + if (!this._sourceManager.checkSourceConfiguration(sourceJsonObject)) { LOGGER.error("La ressource contient une source invalide."); return false; } else { // on stocke l'id de la ressource pour cette source donnée - sourceManager.addUsage(sourceJsonObject.id, resourceJsonObject.resource.id); + this._sourceManager.saveCheckedSource(sourceJsonObject); } // Lien avec la topologie // TODO: vérifier que le type de la topologie soit cohérent avec le type de la source // On stocke la correspondance entre une source et la topologie dont elle dérive - sourceManager.sourceTopology[sourceJsonObject.id] = resourceJsonObject.resource.topology.id; + this._sourceManager.sourceTopology[sourceJsonObject.id] = resourceJsonObject.resource.topology.id; } } - // on sauvegarde l'id de la ressource pour savoir qu'elle a déjà été vérifiée et que sa description est valide - this._listOfVerifiedResourceIds.push(resourceJsonObject.resource.id); + // availableOperations + if (!resourceJsonObject.resource.availableOperations) { + LOGGER.error("La ressource ne contient pas de availableOperations."); + return false; + } else { + // on fait la vérification via le operationManager + if (!this._operationManager.checkResourceOperationConfiguration(resourceJsonObject.resource.availableOperations)) { + LOGGER.error("Mauvaise configuration des operations dans la ressource."); + return false; + } + } + + // On vérifie la cohérence entre les sources diponibles et les opérations configurées + // Pour le moment, on va seulement vérifier que pour chaque opération paramétrée, il y a au moins une source qui puisse répondre + for (let i = 0; i < resourceJsonObject.resource.availableOperations.length; i++) { + + let operationId = resourceJsonObject.resource.availableOperations[i].id; + let found = false; + + for (let j = 0; resourceJsonObject.resource.sources; j++) { + let sourceType = resourceJsonObject.resource.sources[j].type; + let operations = this._sourceManager.operationsByType[sourceType]; + if (operations.includes(operationId)) { + found = true; + break; + } + } + + if (!found) { + LOGGER.error("L'opération " + operationId + " n'a pas de source pour y répondre"); + return false; + } + + } LOGGER.info("Fin de la verification de la ressource."); return true; } - /** * * @function - * @name checkResourceOsrm - * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une ressource osrm. - * @param {json} resourceJsonObject - Description JSON de la ressource - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @name flushCheckedResource + * @description Vider la liste des ressources déjà vérifiées * */ - checkResourceOsrm(resourceJsonObject) { - - LOGGER.info("Verification de la ressource osrm..."); - - // Description - if (!resourceJsonObject.description) { - LOGGER.error("La ressource ne contient pas de description."); - return false; - } else { - // rien à faire - } - - LOGGER.info("Fin de la verification de la ressource osrm."); - return true; + flushCheckedResource() { + this._checkedResourceId = new Array(); + } /** * * @function - * @name checkResourcePgr - * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une ressource pgr. - * @param {json} resourceJsonObject - Description JSON de la ressource - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur - * TODO: c'est une copie conforme de checkResourceOsrm, c'est pas terrible (à factoriser ou spécialiser) + * @name loadResourceDirectory + * @description Fonction utilisée pour charger le contenu d'un dossier de description d'une ressource. + * @param {string} directory - Dossier qui contient les configurations des ressources + * @return {boolean} + * */ - checkResourcePgr(resourceJsonObject) { + loadResourceDirectory(resourceDirectory) { - LOGGER.info("Verification de la ressource pgr..."); + // Pour chaque fichier du dossier des ressources, on crée une ressource + let files = fs.readdirSync(resourceDirectory).filter( (file) => { + return path.extname(file).toLowerCase() === ".resource"; + }); + + for (let fileName of files) { + + let resourceFile = resourceDirectory + "/" + fileName; + LOGGER.info("Chargement de: " + resourceFile); + + // Récupération du contenu en objet pour vérification puis création de la ressource + let resourceContent = {}; + try { + resourceContent = JSON.parse(fs.readFileSync(resourceFile)); + } catch (error) { + LOGGER.error(error); + LOGGER.error("Erreur lors de la lecture de la ressource: " + resourceFile); + } + + // Création de la ressource + if (!this.loadResourceConfiguration(resourceContent)) { + LOGGER.error("La ressource configurée dans le fichier " + resourceFile + " n'a pas pu être chargée"); + } else { + LOGGER.info("Ressource chargée : " + resourceFile); + } - // Description - if (!resourceJsonObject.description) { - LOGGER.error("La ressource ne contient pas de description."); - return false; - } else { - // rien à faire } - LOGGER.info("Fin de la verification de la ressource pgr."); return true; + } /** * * @function - * @name createResource - * @description Fonction utilisée pour créer une ressource. + * @name loadResourceConfiguration + * @description Fonction utilisée pour créer une ressource à partir de sa configuration * @param {json} resourceJsonObject - Description JSON de la ressource - * @param {object} operationManager - Manager d'opération du service - * @return {Resource} Ressource créée + * @return {boolean} * */ - createResource(resourceJsonObject, operationManager) { + loadResourceConfiguration(resourceJsonObject) { let resource; if (!resourceJsonObject.resource.id) { LOGGER.error("La ressource ne contient pas d'id."); - return null; + return false; } LOGGER.info("Creation de la ressource: " + resourceJsonObject.resource.id); - // On vérifie que la ressource a bien été vérifiée et validée - if (this._listOfVerifiedResourceIds.length !== 0) { - for (let i = 0; i < this._listOfVerifiedResourceIds.length; i++ ) { - if (this._listOfVerifiedResourceIds[i] === resourceJsonObject.resource.id) { - LOGGER.info("La ressource contenant l'id " + resourceJsonObject.resource.id + " a deja ete verifiee."); - break; + // On vérifie que la ressource n'existe pas déjà + if (this._loadedResourceId.length !== 0) { + for (let i = 0; i < this._loadedResourceId.length; i++ ) { + if (this._loadedResourceId[i] === resourceJsonObject.resource.id) { + LOGGER.info("La ressource contenant l'id " + resourceJsonObject.resource.id + " a déjà été chargée."); + return true; } } } else { - LOGGER.error("Tentative de creation d'une ressource sans verification prealable. Cette ressource ne peut donc etre creee."); - return null; + // C'est la première ressource créée } - // On vérifie que la ressource n'a pas déjà été créée - if (this._listOfResourceIds.length !== 0) { - for (let i = 0; i < this._listOfResourceIds.length; i++ ) { - if (this._listOfResourceIds[i] === resourceJsonObject.resource.id) { - LOGGER.error("Une ressource contenant l'id " + resourceJsonObject.resource.id + " existe deja. Cette ressource ne peut donc etre creee."); - return null; - } - } + // Création de la topology associée + LOGGER.info("Chargement de la topology associé..."); + let currentTopology = {}; + if (!this._topologyManager.loadTopologyConfiguration(resourceJsonObject.resource.topology)) { + LOGGER.error("Impossible de créer la topology associée à la ressource"); + return false; } else { - // C'est la première ressource. + currentTopology = this._topologyManager.getTopology(resourceJsonObject.resource.topology.id); } - // Création des opérations - // --- + // Création des sources associées + LOGGER.info("Chargement des sources associées..."); + for (let i = 0; i < resourceJsonObject.resource.sources.length; i++) { + if (!this._sourceManager.loadSourceConfiguration(resourceJsonObject.resource.sources[i], currentTopology)) { + LOGGER.error("Impossible de créer la source associée à la ressource : " + resourceJsonObject.resource.sources[i].id); + return false; + } + } + // Création des opérations let resourceOperationHash = {}; - - if (!operationManager.createResourceOperation(resourceOperationHash, resourceJsonObject)) { + if (!this._operationManager.loadResourceOperationConfiguration(resourceOperationHash, resourceJsonObject)) { LOGGER.error("Erreur lors de la creation des operations de la ressource"); - return null; - } else { - // on continue - } - - // --- + return false; + } + // Création de la ressource if (resourceJsonObject.resource.type === "osrm") { resource = new osrmResource(resourceJsonObject, resourceOperationHash); } else if (resourceJsonObject.resource.type === "pgr") { @@ -354,13 +418,16 @@ module.exports = class resourceManager { } else if (resourceJsonObject.resource.type === "smartpgr") { resource = new smartpgrResource(resourceJsonObject, resourceOperationHash); } else { - // On va voir si c'est un autre type. + LOGGER.error("Type de la ressource inconnue"); + return false; } - // on sauvegarde l'id de la ressource pour savoir qu'elle a déjà été créée - this._listOfResourceIds.push(resourceJsonObject.resource.id); + // on sauvegarde l'id de la ressource pour savoir qu'elle a déjà été créée et la ressource elle-même + this._loadedResourceId.push(resourceJsonObject.resource.id); + this._resource[resourceJsonObject.resource.id] = resource; - return resource; + return true; + } diff --git a/src/js/resources/smartpgrResource.js b/src/js/resources/smartpgrResource.js index 7f9ff42..c08a9e6 100644 --- a/src/js/resources/smartpgrResource.js +++ b/src/js/resources/smartpgrResource.js @@ -158,30 +158,4 @@ module.exports = class smartpgrResource extends Resource { } - /** - * - * @function - * @name removeSource - * @description Supprimer les references à une source au sein de la ressource - * @param {string} sourceId - Id de la source - * @return {boolean} - * - */ - removeSource (sourceId) { - - let keysToDelete = new Array(); - for (const key in this._linkedSource) { - if (this._linkedSource[key] === sourceId) { - keysToDelete.push(key); - } - } - if(keysToDelete.length !== 0) { - for (let i = 0; i < keysToDelete.length; i++) { - delete this._linkedSource[keysToDelete[i]]; - } - } - - return true; - } - } diff --git a/src/js/road2.js b/src/js/road2.js index ca753dd..56a2f48 100644 --- a/src/js/road2.js +++ b/src/js/road2.js @@ -2,7 +2,7 @@ const log4js = require('log4js'); const nconf = require('nconf'); -const Service = require('./service/service'); +const Administrator = require('./administrator/administrator'); const path = require('path'); const fs = require('fs'); const pm = require('./utils/processManager.js'); @@ -21,7 +21,7 @@ var LOGGER; async function start() { console.log("==========================="); - console.log("ROAD2 - Calcul d'itineraire"); + console.log("ROAD2 - Administrator"); console.log("==========================="); // Chargement de la configuration @@ -32,61 +32,85 @@ async function start() { let logConfiguration = getLoggerConfiguration(configuration, configurationPath); checkAndInitLogger(logConfiguration); - // Création du service - let service = new Service(); + LOGGER.info("Logger de l'administrateur initialisé"); - // Vérification de la configuration globale du service et sauvegarde - if (!service.checkAndSaveGlobalConfiguration(configuration, configurationPath)) { - pm.shutdown(1); - } + // Création de l'administrateur car il sera utilisé dans tous les cas + let administrator = new Administrator(); - // Sauvegarde de la configuration des logs - service.logConfiguration = logConfiguration; + if (!nconf.argv().get('configCheck')) { - // Chargement des opérations rendues disponibles sur le service - if (!service.loadOperations()) { - pm.shutdown(1); - } + // Cas général de l'utilisation de Road2 + LOGGER.info("Lancement classique de Road2"); - // Chargement des projections - if (!service.loadProjections()) { - pm.shutdown(1); - } + // Récupération de la configuration de l'administrateur + if (!administrator.checkAdminConfiguration(configuration, configurationPath)) { + LOGGER.fatal("La configuration de l'administrateur n'est pas validée"); + pm.shutdown(11); + } else { - // Chargement des ressources - if (!(await service.loadResources())) { - pm.shutdown(1); - } + LOGGER.info("La configuration de l'administrateur est validée. On la sauvegarde."); - // En mode check de configuration, fermeteure du serveur sans code d'erreur - if (nconf.argv().get('configCheck')) { + // On aura besoin de cette configuration plus tard + administrator.saveAdminConfiguration(configuration, configurationPath, logConfiguration); - LOGGER.info("La vérification de la configuration est terminée"); - pm.shutdown(0); + // Création du serveur d'administration + if (!administrator.createServer()) { + LOGGER.fatal("Le serveur de l'administrateur ne peut être créé"); + pm.shutdown(12); + } else { + + LOGGER.debug("Serveur administrateur créé"); - } else { + // Création des services au démarrage si demandé + LOGGER.info("Création des services demandés au démarrage..."); - // Chargement des topologies - if (!service.loadTopologies()) { - pm.shutdown(1); - } + if (!(await administrator.createServicesOnStart())) { + LOGGER.error("Problèmes lors du démarrage des services demandés. Reconfigurez les et relancez leur démarrage."); + // On n'éteint pas le serveur d'administration car les services pourront être reconfiguré et démarrés par l'API + } else { + LOGGER.info("Les services concernés ont été démarré"); + } + + } - // Chargement des sources uniques - try { - await service.loadSources(); - } catch (err) { - LOGGER.fatal("Impossible de charger les sources", err); - pm.shutdown(1); } - // Création du serveur web - if (!service.createServer("../apis/", "")) { - pm.shutdown(1); + } else { + + // Cas particulier + LOGGER.info("Lancement de Road2 pour vérification des configurations"); + + // On commence par vérifier la configuration du service + if (!administrator.checkAdminConfiguration(configuration, configurationPath)) { + + // La configuration de l'administrateur n'est pas validée + LOGGER.fatal("La configuration de l'administrateur n'est pas validée"); + pm.shutdown(11); + + } else { + + LOGGER.info("La configuration de l'administrateur a été vérifiée et validée"); + + // On la sauvegarde pour la suite + administrator.saveAdminConfiguration(configuration, configurationPath, logConfiguration); + + // On vérifie la configuration du service + if (!(await administrator.checkServicesConfiguration())) { + + // La configuration du service n'est pas validée + LOGGER.fatal("La configuration des services n'est pas validée"); + pm.shutdown(1); + + } else { + LOGGER.info("La configuration des services est validée"); + } + } - } + LOGGER.info("La vérification des différentes configurations est terminée"); + pm.shutdown(0); - + } } @@ -120,31 +144,31 @@ function loadGlobalConfiguration() { } catch (error) { console.log("Impossible de recuperer le chemin absolu du fichier de configuration:"); console.log(error); - process.exit(1); + process.exit(11); } // vérification de l'exitence du fichier if (fs.existsSync(configurationPath)) { - // chargement dans une variable pour la classe Service + // chargement dans une variable pour la classe Administrateur try { globalConfiguration = JSON.parse(fs.readFileSync(configurationPath)); } catch (error) { console.log("Mauvaise configuration: impossible de lire ou de parser le fichier de configuration de Road2:"); console.log(error); - process.exit(1); + process.exit(11); } } else { console.log("Mauvaise configuration: fichier de configuration global inexistant: " + configurationPath); console.log("Utilisez le paramètre ROAD2_CONF_FILE en ligne de commande ou en variable d'environnement pour le préciser."); - process.exit(1); + process.exit(11); } } else { //si aucun fichier n'a été précisé on renvoie une erreur console.log("Aucun fichier de configuration. Utiliser la variable d'environnement $ROAD2_CONF_FILE ou l'option --ROAD2_CONF_FILE lors de l'initialisation du serveur."); - process.exit(1); + process.exit(11); } console.log("Configuration chargee.") @@ -156,9 +180,9 @@ function loadGlobalConfiguration() { /** * * @function -* @name initLogger +* @name checkAndInitLogger * @description Initialiser le logger -* @param {json} userLogConfigurationFile - Configuration des logs de l'application +* @param {json} userLogConfiguration - Configuration des logs de l'administrateur * */ @@ -176,7 +200,7 @@ function checkAndInitLogger(userLogConfiguration) { } catch (error) { console.log("Mauvaise configuration des logs dans mainConf"); console.log(error); - process.exit(1); + process.exit(11); } //Instanciation du logger @@ -186,39 +210,39 @@ function checkAndInitLogger(userLogConfiguration) { } else { console.log("Mausvaise configuration pour les logs: 'mainConf' absent."); - process.exit(1); + process.exit(11); } if (userLogConfiguration.httpConf) { if (!userLogConfiguration.httpConf.level) { console.log("Mausvaise configuration pour les logs: 'httpConf.level' absent."); - process.exit(1); + process.exit(11); } else { if (typeof userLogConfiguration.httpConf.level !== "string") { console.log("Mausvaise configuration pour les logs: 'httpConf.level' n'est pas une chaine de caracteres"); - process.exit(1); + process.exit(11); } } if (!userLogConfiguration.httpConf.format) { console.log("Mausvaise configuration pour les logs: 'httpConf.format' absent."); - process.exit(1); + process.exit(11); } else { if (typeof userLogConfiguration.httpConf.format !== "string") { console.log("Mausvaise configuration pour les logs: 'httpConf.format' n'est pas une chaine de caracteres"); - process.exit(1); + process.exit(11); } } } else { console.log("Mausvaise configuration pour les logs: 'httpConf' absent."); - process.exit(1); + process.exit(11); } } else { console.log("Aucune configuration pour les logs."); - process.exit(1); + process.exit(11); } } @@ -227,9 +251,9 @@ function checkAndInitLogger(userLogConfiguration) { * * @function * @name getLoggerConfiguration -* @description Récupérer la configuration des logs du serveur -* @param {json} userConfiguration - Configuration de l'application -* @param {string} userConfigurationPath - CHemin absolu du fichier de configuration +* @description Récupérer la configuration des logs du serveur d'administration +* @param {json} userConfiguration - Configuration de l'administrateur +* @param {string} userConfigurationPath - Chemin absolu du fichier de configuration * @return {json} Configuration des logs du serveur * */ @@ -243,13 +267,13 @@ function getLoggerConfiguration(userConfiguration, userConfigurationPath) { if (userConfiguration) { - if (userConfiguration.application) { + if (userConfiguration.administration) { - if (userConfiguration.application.logs) { + if (userConfiguration.administration.logs) { - if (userConfiguration.application.logs.configuration) { + if (userConfiguration.administration.logs.configuration) { - userLogConfigurationFile = userConfiguration.application.logs.configuration; + userLogConfigurationFile = userConfiguration.administration.logs.configuration; // chemin absolu du fichier let file = ""; @@ -262,7 +286,7 @@ function getLoggerConfiguration(userConfiguration, userConfigurationPath) { console.log("Impossible de recuperer le chemin absolu du fichier de log:"); console.log(error); - process.exit(1); + process.exit(11); } @@ -274,33 +298,33 @@ function getLoggerConfiguration(userConfiguration, userConfigurationPath) { } catch (error) { console.log("Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs:"); console.log(error); - process.exit(1); + process.exit(11); } } else { console.log("Mauvaise configuration: fichier de configuration des logs inexistant:"); console.log(file); - process.exit(1); + process.exit(11); } } else { - console.log("Mauvais configuration: 'application.logs.configuration' absent."); - process.exit(1); + console.log("Mauvaise configuration: 'administration.logs.configuration' absent."); + process.exit(11); } } else { - console.log("Mauvais configuration: 'application.logs' absent."); - process.exit(1); + console.log("Mauvaise configuration: 'administration.logs' absent."); + process.exit(11); } } else { - console.log("Mauvais configuration: 'application' absent."); - process.exit(1); + console.log("Mauvaise configuration: 'administration' absent."); + process.exit(11); } } else { // cela ne doit arriver que si cette fonction est appelée sans paramètre - console.log("Absence de configuration pour l'application."); - process.exit(1); + console.log("Absence de configuration pour l'administration."); + process.exit(11); } return logsConf; diff --git a/src/js/server/serverManager.js b/src/js/server/serverManager.js index 3c26ff0..964d9b0 100644 --- a/src/js/server/serverManager.js +++ b/src/js/server/serverManager.js @@ -3,7 +3,6 @@ const Server = require('./server'); const log4js = require('log4js'); const fs = require('fs'); -const path = require('path'); const assert = require('assert'); // Création du LOGGER @@ -29,22 +28,32 @@ module.exports = class serverManager { */ constructor() { + // Liste des serveurs chargés + this._loadedServerId = new Array(); + + // Liste des serveurs vérifiés + this._checkedServerId = new Array(); + // Catalogue de serveurs this._serverCatalog = {}; // Description des serveurs - this._serverDescriptions = new Array(); + this._loadedServerDescription = {}; + + // Description des serveurs + this._checkedServerDescription = {}; } /** * * @function - * @name checkConfiguration + * @name checkServerConfiguration * @description Vérifier la configuration d'un serveur + * @param {object} config - Configuration à vérifier * */ - checkConfiguration(config) { + checkServerConfiguration(config) { if (!config) { LOGGER.error("Aucune configuration n'a ete fournie"); @@ -57,7 +66,27 @@ module.exports = class serverManager { LOGGER.error("La configuration du serveur n'indique aucun id"); return false; } else { - // TODO: Vérification + + // On vérifie que l'id n'est pas déjà chargé + if (this._loadedServerId.length !== 0) { + for (let i = 0; i < this._loadedServerId.length; i++) { + if (config.id === this._loadedServerId[i]) { + LOGGER.error("Un serveur contenant l'id " + config.id + " est deja chargé."); + return false; + } + } + } + + // On vérifie que l'id n'est pas déjà pris par le check courant + if (this._checkedServerId.length !== 0) { + for (let i = 0; i < this._checkedServerId.length; i++) { + if (config.id === this._checkedServerId[i]) { + LOGGER.error("Un serveur contenant l'id " + config.id + " est déjà verifié."); + return false; + } + } + } + } if (!config.https) { @@ -176,69 +205,79 @@ module.exports = class serverManager { } - // Stockage de la description - this._serverDescriptions.push(config); - return true; } + saveServerConfiguration(config) { + + this._checkedServerId.push(config.id); + this._checkedServerDescription[config.id] = config; + + } + /** * * @function - * @name createServer + * @name loadServerConfiguration * @description Créer un serveur + * @param {object} app - Instance d'ExpressJS + * @param {object} config - Configuration du serveur * */ - createServer(app, config) { + loadServerConfiguration(app, config) { + + LOGGER.info("Chargement du serveur : " + config.id); + + // On vérifie que l'id n'est pas déjà chargé + if (this._loadedServerId.length !== 0) { + for (let i = 0; i < this._loadedServerId.length; i++) { + if (config.id === this._loadedServerId[i]) { + LOGGER.info("Un serveur contenant l'id " + config.id + " est deja chargé."); + return true; + } + } + } if (config.https === "true") { - return new Server(config.id, app, config.host, config.port, "true", config.options); + this._serverCatalog[config.id] = new Server(config.id, app, config.host, config.port, "true", config.options); } else { - return new Server(config.id, app, config.host, config.port, "false", config.options); + this._serverCatalog[config.id] = new Server(config.id, app, config.host, config.port, "false", config.options); } + this._loadedServerId.push(config.id); + this._loadedServerDescription[config.id] = config; + + return true; + } /** * * @function - * @name createAllServer - * @description Créer l'ensemble des serveurs disponibles dans le manager + * @name flushCheckedServer + * @description Vider la liste des serveurs déjà vérifiées * */ - createAllServer(app) { + flushCheckedServer() { - if (this._serverDescriptions.length === 0) { - LOGGER.error("Aucun serveur n'est disponible."); - return false; - } - - for(let i = 0; i < this._serverDescriptions.length; i++) { - let configuration = this._serverDescriptions[i]; - try { - this._serverCatalog[configuration.id] = this.createServer(app, configuration); - } catch (err) { - return false; - } - } - - return true; + this._checkedServerId = new Array(); + this._checkedServerDescription = {}; } /** * * @function - * @name startAllServer - * @description Démarer l'ensemble des serveurs disponibles dans le manager + * @name startAllServers + * @description Démarrer l'ensemble des serveurs disponibles dans le manager * */ - startAllServer() { + startAllServers() { LOGGER.info("Demarrage de l'ensemble des serveurs."); - if (this._serverDescriptions.length === 0) { + if (this._loadedServerId.length === 0) { LOGGER.error("Aucun serveur n'est disponible."); return false; } @@ -276,7 +315,7 @@ module.exports = class serverManager { LOGGER.info("Arret de l'ensemble des serveurs."); - if (this._serverDescriptions.length === 0) { + if (this._loadedServerId.length === 0) { LOGGER.warn("Aucun serveur n'est disponible."); return true; } diff --git a/src/js/service/main.js b/src/js/service/main.js new file mode 100644 index 0000000..4c0aec4 --- /dev/null +++ b/src/js/service/main.js @@ -0,0 +1,250 @@ +'use strict'; + +const pm = require('../utils/processManager.js'); +const Service = require('./service'); +const log4js = require('log4js'); +const fs = require('fs'); +const path = require('path'); + +var LOGGER; + +/** +* +* @function +* @name startService +* @description Fonction principale pour démarrer un service +* @param {string} configurationLocation - Localisation de la configuration du service (localisation absolue du service.json) +* +*/ + +async function startService() { + + console.log("==========================="); + console.log("ROAD2 - Service "); + console.log("==========================="); + + // Récupération des arguments + let configurationLocation = ""; + if (process.argv[2] !== "") { + configurationLocation = process.argv[2]; + } else { + console.log("Configuration du service absente, extinction du processus..."); + process.exit(1); + } + + // Récupération de la configuration + let configuration = ""; + try { + configuration = JSON.parse(fs.readFileSync(configurationLocation)); + } catch(error) { + console.log("Impossible de lire la configuration : " + configurationLocation); + console.log(error); + process.exit(1); + } + + // Instanciation du logger + let logConfiguration = getLoggerConfiguration(configuration, configurationLocation); + checkAndInitLogger(logConfiguration); + + LOGGER.info("Instanciation du logger pour le service terminee. Creation de la classe Service..."); + + // Création du service + let service = new Service(); + + // Vérification de la configuration globale du service + // TODO : Selon les erreurs, on lance ou pas le service + + if (!(await service.checkServiceConfiguration(configuration, configurationLocation))) { + pm.shutdown(1); + } else { + + LOGGER.debug("Configuration du service vérifiée et suffisamment validée pour lancer un service"); + + // On aura besoin de cette configuration juste après et peut-être plus tard + service.saveServiceConfiguration(configuration, configurationLocation, logConfiguration); + + // On lance le chargement du service qui est normalement correctement configuré + if (!service.loadServiceConfiguration()) { + pm.shutdown(2); + } else { + + LOGGER.debug("Service chargé"); + + // On connecte les sources + if (!(await service.connectSources())) { + + LOGGER.fatal("Aucune source n'a pu être connectée"); + pm.shutdown(4); + + } else { + + LOGGER.info("Les sources connectables ont été connectées"); + + // On démarre les serveurs associé à ce service + if (!service.startServers()) { + pm.shutdown(5); + } + + } + + } + + } + +} + +/** +* +* @function +* @name checkAndInitLogger +* @description Initialiser le logger +* @param {json} userLogConfiguration - Configuration des logs de l'administrateur +* +*/ + +function checkAndInitLogger(userLogConfiguration) { + + console.log("Instanciation du logger..."); + + if (userLogConfiguration) { + + if (userLogConfiguration.mainConf) { + + // Configuration du logger + try { + log4js.configure(userLogConfiguration.mainConf); + } catch (error) { + console.log("Mauvaise configuration des logs dans mainConf"); + console.log(error); + process.exit(1); + } + + //Instanciation du logger + LOGGER = log4js.getLogger('MAIN-SERVICE'); + + LOGGER.info("Logger charge."); + + } else { + console.log("Mausvaise configuration pour les logs: 'mainConf' absent."); + process.exit(1); + } + + if (userLogConfiguration.httpConf) { + + if (!userLogConfiguration.httpConf.level) { + console.log("Mausvaise configuration pour les logs: 'httpConf.level' absent."); + process.exit(1); + } else { + if (typeof userLogConfiguration.httpConf.level !== "string") { + console.log("Mausvaise configuration pour les logs: 'httpConf.level' n'est pas une chaine de caracteres"); + process.exit(1); + } + } + + if (!userLogConfiguration.httpConf.format) { + console.log("Mausvaise configuration pour les logs: 'httpConf.format' absent."); + process.exit(1); + } else { + if (typeof userLogConfiguration.httpConf.format !== "string") { + console.log("Mausvaise configuration pour les logs: 'httpConf.format' n'est pas une chaine de caracteres"); + process.exit(1); + } + } + + } else { + console.log("Mausvaise configuration pour les logs: 'httpConf' absent."); + process.exit(1); + } + + } else { + console.log("Aucune configuration pour les logs."); + process.exit(1); + } + + } + + /** + * + * @function + * @name getLoggerConfiguration + * @description Récupérer la configuration des logs du serveur d'administration + * @param {json} userConfiguration - Configuration de l'administrateur + * @param {string} userConfigurationPath - Chemin absolu du fichier de configuration + * @return {json} Configuration des logs du serveur + * + */ + + function getLoggerConfiguration(userConfiguration, userConfigurationPath) { + + console.log("Recuperation de la configuration du logger..."); + + let logsConf; + let userLogConfigurationFile; + + if (userConfiguration) { + + if (userConfiguration.application) { + + if (userConfiguration.application.logs) { + + if (userConfiguration.application.logs.configuration) { + + userLogConfigurationFile = userConfiguration.application.logs.configuration; + + // chemin absolu du fichier + let file = ""; + + try { + + file = path.resolve(path.dirname(userConfigurationPath), userLogConfigurationFile); + + } catch (error) { + + console.log("Impossible de recuperer le chemin absolu du fichier de log:"); + console.log(error); + process.exit(1); + + } + + // vérification de l'exitence du fichier + if (fs.existsSync(file)) { + //Lecture du fichier de configuration des logs + try { + logsConf = JSON.parse(fs.readFileSync(file)); + } catch (error) { + console.log("Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs:"); + console.log(error); + process.exit(1); + } + } else { + console.log("Mauvaise configuration: fichier de configuration des logs inexistant:"); + console.log(file); + process.exit(1); + } + + } else { + console.log("Mauvaise configuration: 'application.logs.configuration' absent."); + process.exit(1); + } + + } else { + console.log("Mauvaise configuration: 'application.logs' absent."); + process.exit(1); + } + + } else { + console.log("Mauvaise configuration: 'application' absent."); + process.exit(1); + } + + } else { + // cela ne doit arriver que si cette fonction est appelée sans paramètre + console.log("Absence de configuration pour l'application."); + process.exit(1); + } + + return logsConf; + + } + +startService(); \ No newline at end of file diff --git a/src/js/service/service.js b/src/js/service/service.js index 244e6e7..899ca66 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -3,18 +3,17 @@ const fs = require('fs'); const path = require('path'); const express = require('express'); -const assert = require('assert').strict; const cors = require('cors'); const helmet = require('helmet'); const ApisManager = require('../apis/apisManager'); const ResourceManager = require('../resources/resourceManager'); -const errorManager = require('../utils/errorManager'); const SourceManager = require('../sources/sourceManager'); const OperationManager = require('../operations/operationManager'); const BaseManager = require('../base/baseManager'); const TopologyManager = require('../topology/topologyManager'); const ProjectionManager = require('../geography/projectionManager'); const ServerManager = require('../server/serverManager'); +const LogManager = require('../utils/logManager'); const log4js = require('log4js'); // Création du LOGGER @@ -52,17 +51,11 @@ module.exports = class Service { // Manager des topologies du service this._topologyManager = new TopologyManager(this._baseManager, this._projectionManager); - // Manager des ressources du service. - this._resourceManager = new ResourceManager(); - - // catalogue des ressources du service. - this._resourceCatalog = {}; - // Manager des sources du service. this._sourceManager = new SourceManager(); - // catalogue des sources du service. - this._sourceCatalog = {}; + // Manager des ressources du service. + this._resourceManager = new ResourceManager(this._sourceManager, this._operationManager, this._topologyManager); // Manager des apis du service this._apisManager = new ApisManager(); @@ -84,8 +77,8 @@ module.exports = class Service { /** * * @function - * @name get resourceCatalog - * @description Récupérer l'ensemble des ressources + * @name get configuration + * @description Récupérer la configuration du service * */ get configuration() { @@ -124,17 +117,6 @@ module.exports = class Service { } } - /** - * - * @function - * @name get resourceCatalog - * @description Récupérer l'ensemble des ressources - * - */ - get resourceCatalog() { - return this._resourceCatalog; - } - /** * * @function @@ -167,7 +149,18 @@ module.exports = class Service { * */ getResourceById(id) { - return this._resourceCatalog[id]; + return this._resourceManager.resource[id]; + } + + /** + * + * @function + * @name getResources + * @description Récupérer l'ensemble des ressources + * + */ + getResources() { + return this._resourceManager.resource; } /** @@ -179,24 +172,13 @@ module.exports = class Service { * */ verifyResourceExistenceById(id) { - if (this._resourceCatalog[id]) { + if (this._resourceManager.resource[id]) { return true; } else { return false; } } - /** - * - * @function - * @name get sourceCatalog - * @description Récupérer l'ensemble des sources - * - */ - get sourceCatalog() { - return this._sourceCatalog; - } - /** * * @function @@ -206,7 +188,7 @@ module.exports = class Service { * */ getSourceById(id) { - return this._sourceCatalog[id]; + return this._sourceManager.source[id]; } /** @@ -218,7 +200,7 @@ module.exports = class Service { * */ verifySourceExistenceById(id) { - if (this._sourceCatalog[id]) { + if (this._sourceManager.source[id]) { return true; } else { return false; @@ -239,39 +221,49 @@ module.exports = class Service { /** * * @function - * @name checkAndSaveGlobalConfiguration + * @name checkServiceConfiguration * @description Vérification de la configuration globale du serveur * @param {json} userConfiguration - JSON décrivant la configuration du service * @param {string} userConfigurationPath - Chemin absolu du fichier de configuration * */ - checkAndSaveGlobalConfiguration(userConfiguration, userConfigurationPath) { + async checkServiceConfiguration(userConfiguration, userConfigurationPath) { + + LOGGER.info("Verification de la configuration globale du service..."); - LOGGER.info("Verification de la configuration globale de l'application..."); + LOGGER.debug("Vérification des informations générales"); // Configuration de l'application if (!userConfiguration.application) { LOGGER.fatal("Mauvaise configuration: Objet 'application' manquant !"); return false; } + // Nom de l'application if (!userConfiguration.application.name) { LOGGER.fatal("Mauvaise configuration: Champ 'application:name' manquant !"); return false; } + // Titre de l'application if (!userConfiguration.application.title) { LOGGER.fatal("Mauvaise configuration: Champ 'application:title' manquant !"); return false; } + // Description de l'application if (!userConfiguration.application.description) { LOGGER.fatal("Mauvaise configuration: Champ 'application:description' manquant !"); return false; } + // Information sur le fournisseur du service + + LOGGER.debug("Vérification des informations du provider"); + if (userConfiguration.application.provider) { + // Nom if (!userConfiguration.application.provider.name) { LOGGER.fatal("Mauvaise configuration: Champ 'application:provider:name' manquant !"); @@ -286,121 +278,162 @@ module.exports = class Service { LOGGER.fatal("Mauvaise configuration: Champ 'application:provider:mail' manquant !"); return false; } + } else { LOGGER.warn("Configuration incomplete: Objet 'application:provider' manquant !"); } + + // Information sur le logger + + // On le test ici aussi pour avoir un check le plus complet quand il est lancé en dehors d'un démarrage de Road2 + LOGGER.debug("Vérification des informations sur les logs"); + + if (!userConfiguration.application.logs) { + LOGGER.error("Mauvaise configuration: Objet 'application.logs' manquant !"); + return false; + } else { + + if (!userConfiguration.application.logs.configuration) { + LOGGER.error("Mauvaise configuration: Objet 'application.logs.configuration' manquant !"); + return false; + } else { + + let logConfPath = ""; + + try { + logConfPath = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.logs.configuration); + } catch (error) { + LOGGER.error("Impossible d'avoir le chemin absolu du fichier de configuration des logs: " + userConfiguration.application.logs.configuration); + LOGGER.error(error); + return false; + } + + if (fs.existsSync(logConfPath)) { + + let logConf = {}; + + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + logConf = JSON.parse(fs.readFileSync(logConfPath)); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de conf des logs du service de Road2: " + logConfPath); + LOGGER.error(error); + return false; + } + + if (!LogManager.checkLogConfiguration(logConf, logConfPath)) { + LOGGER.error("Le logger est mal configuré"); + return false; + } + + } else { + LOGGER.fatal("Mauvaise configuration: Fichier de conf des logs inexistant : " + logConfPath); + return false; + } + + } + } + // Information sur les opérations - if (userConfiguration.application.operations) { + + LOGGER.debug("Vérification des informations sur les opérations"); + + if (!userConfiguration.application.operations) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:operations' manquant !"); + return false; + } else { + // Dossier contenant les fichiers d'opérations if (!userConfiguration.application.operations.directory) { LOGGER.fatal("Mauvaise configuration: Champ 'application:operations:directory' manquant !"); return false; } else { - // On vérifie que le dossier existe et qu'il contient des fichiers de description des opérations - let directory = ""; - - try { - directory = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.operations.directory); - } catch (error) { - LOGGER.fatal("Can't get absolute path of operations directory: " + userConfiguration.application.operations.directory); - LOGGER.fatal(error); + // On vérifie d'abord les paramètres car ils sont référencés dans les opérations + if (!userConfiguration.application.operations.parameters) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:operations:parameters' manquant !"); return false; - } - - if (fs.existsSync(directory)) { - // On vérifie que l'application peut lire les fichiers du dossier - fs.readdirSync(directory).forEach(operation => { + } else { - let operationFile = ""; + if (!userConfiguration.application.operations.parameters.directory) { + LOGGER.fatal("Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !"); + return false; + } else { - try { - operationFile = directory + "/" + operation; - fs.accessSync(operationFile, fs.constants.R_OK); - } catch (err) { - LOGGER.error("Le fichier d'operation ne peut etre lu: " + operationFile); - } + // On vérifie que le dossier existe et qu'il contient des fichiers de description des paramètres + let parameterDirectory = ""; try { - // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard - JSON.parse(fs.readFileSync(operationFile)); + parameterDirectory = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.operations.parameters.directory); } catch (error) { - LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier d'operation: " + operationFile); - LOGGER.error(error); + LOGGER.fatal("Can't get absolute path of parameters directory: " + userConfiguration.application.operations.parameters.directory); + LOGGER.fatal(error); return false; } - }); + if (!this._operationManager.checkParameterDirectory(parameterDirectory)) { + LOGGER.error("Le dossier des parametres est mal configuré"); + return false; + } - // On vérifie que la partie concernant les paramètres est bien renseignée - if (!userConfiguration.application.operations.parameters) { - LOGGER.fatal("Mauvaise configuration: Objet 'application:operations:parameters' manquant !"); - return false; - } else { + // On vérifie que le dossier existe et qu'il contient des fichiers de description des opérations + let operationDirectory = ""; - if (!userConfiguration.application.operations.parameters.directory) { - LOGGER.fatal("Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !"); + try { + operationDirectory = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.operations.directory); + } catch (error) { + LOGGER.fatal("Can't get absolute path of operations directory: " + userConfiguration.application.operations.directory); + LOGGER.fatal(error); return false; - } else { - - // On vérifie que le dossier existe et qu'il contient des fichiers de description des paramètres - let directory = ""; - - try { - directory = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.operations.parameters.directory); - } catch (error) { - LOGGER.fatal("Can't get absolute path of parameters directory: " + userConfiguration.application.operations.parameters.directory); - LOGGER.fatal(error); - return false; - } - - if (fs.existsSync(directory)) { - // On vérifie que l'application peut lire les fichiers du dossier - fs.readdirSync(directory).forEach(parameter => { - - let parameterFile = ""; - - try { - parameterFile = directory + "/" + parameter; - fs.accessSync(parameterFile, fs.constants.R_OK); - } catch (err) { - LOGGER.error("Le fichier de parametres ne peut etre lu: " + parameterFile); - } - - try { - // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard - JSON.parse(fs.readFileSync(parameterFile)); - } catch (error) { - LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de parametres: " + parameterFile); - LOGGER.error(error); - return false; - } - - }); - - } else { - LOGGER.fatal("Mauvaise configuration: Le dossier des parametres n'existe pas : " + directory); - return false; - } + } + if (!this._operationManager.checkOperationDirectory(operationDirectory)) { + LOGGER.error("Le dossier des operations est mal configuré"); + return false; } } - } else { - LOGGER.fatal("Mauvaise configuration: Le dossier des operations n'existe pas: " + directory); - return false; } } - } else { - LOGGER.fatal("Mauvaise configuration: Objet 'application:operations' manquant !"); + } + + // Projections + + LOGGER.debug("Vérification des informations sur les projections"); + + if (!userConfiguration.application.projections) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:projections' manquant !"); return false; + } else { + + if (!userConfiguration.application.projections.directory) { + LOGGER.fatal("Mauvaise configuration: Champ 'application:projections:directory' manquant !"); + return false; + } else { + + + let directory = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.projections.directory); + + if (!this._projectionManager.checkProjectionDirectory(directory)) { + LOGGER.fatal("La configuration des projections du dossier est incorrecte"); + return false; + } + + } + } // Information sur les ressources - if (userConfiguration.application.resources) { + + LOGGER.debug("Vérification des informations sur les ressources"); + + if (!userConfiguration.application.resources) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:resources' manquant !"); + return false; + } else { // Dossier contenant les fichiers de ressources if (!userConfiguration.application.resources.directories) { LOGGER.fatal("Mauvaise configuration: Champ 'application:resources:directories' manquant !"); @@ -418,7 +451,8 @@ module.exports = class Service { return false; } - let resourceCount = 0; + let oneValidDir = false; + for (let i = 0; i < resourcesDirectories.length; i++) { // On vérifie que le dossier existe et qu'il contient des fichiers de description des ressources @@ -428,49 +462,32 @@ module.exports = class Service { } else { let directory = path.resolve(path.dirname(userConfigurationPath), resourcesDirectories[i]); - if (fs.existsSync(directory)) { - // On vérifie que l'application peut lire les fichiers du dossier - fs.readdirSync(directory).forEach(resource => { - - let resourceFile = ""; - try { - resourceFile = directory + "/" + resource; - fs.accessSync(resourceFile, fs.constants.R_OK); - resourceCount++; - } catch (err) { - LOGGER.error("Le fichier de ressource ne peut etre lu: " + resourceFile); - } - - try { - // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard - JSON.parse(fs.readFileSync(resourceFile)); - } catch (error) { - LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de ressource: " + resourceFile); - LOGGER.error(error); - return false; - } - - }); + + if (!(await this._resourceManager.checkResourceDirectory(directory))) { + LOGGER.error("Le dossier " + directory + " contient des ressources dont la vérification a échoué"); + return false; } else { - LOGGER.error("Mauvaise configuration: Le dossier n'existe pas: " + directory ); + LOGGER.info("Le dossier " + directory + " est vérifié et suffisamment validé pour continuer"); + oneValidDir = true; } } } - if (resourceCount === 0) { - LOGGER.fatal("Mauvaise configuration: Champ 'application:resources:directories' ne pointe vers aucune ressource disponible !"); + if (!oneValidDir) { + LOGGER.error("Aucun dossier de ressource n'a été validé"); return false; } } - } else { - LOGGER.fatal("Mauvaise configuration: Objet 'application:resources' manquant !"); - return false; - } + } + // Information sur le reseau + + LOGGER.debug("Vérification des informations sur le réseau"); + if (!userConfiguration.application.network) { LOGGER.fatal("Mauvaise configuration: Objet 'application:network' manquant !"); return false; @@ -492,9 +509,11 @@ module.exports = class Service { } for (let i = 0; i < userConfiguration.application.network.servers.length; i++) { - if (!this._serverManager.checkConfiguration(userConfiguration.application.network.servers[i])) { + if (!this._serverManager.checkServerConfiguration(userConfiguration.application.network.servers[i])) { LOGGER.fatal("Mauvaise configuration d'un serveur !"); return false; + } else { + this._serverManager.saveServerConfiguration(userConfiguration.application.network.servers[i]); } } @@ -539,351 +558,163 @@ module.exports = class Service { } - if (!userConfiguration.application.projections) { - LOGGER.fatal("Mauvaise configuration: Objet 'application:projections' manquant !"); + // Les APIs + + LOGGER.debug("Vérification des informations sur les APIs"); + + if (!userConfiguration.application.apis) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:apis' manquant !"); return false; } else { - if (!userConfiguration.application.projections.directory) { - LOGGER.fatal("Mauvaise configuration: Champ 'application:projections:directory' manquant !"); - return false; + if (!Array.isArray(userConfiguration.application.apis)) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:apis' n'est pas un tableau !"); + return false; + } + + if (userConfiguration.application.apis.length === 0) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:apis' est un tableau vide !"); + return false; } else { - let projDir = ""; - - try { - projDir = path.resolve(path.dirname(userConfigurationPath), userConfiguration.application.projections.directory); - } catch (error) { - LOGGER.error("Impossible d'avoir le chemin aboslu du dossier de projection: " + projDir); - LOGGER.error(error); - return false; + for (let i = 0; i < userConfiguration.application.apis.length; i++) { + if (!this._apisManager.checkApiConfiguration(userConfiguration.application.apis[i])) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:apis' contient un objet invalide"); + return false; + } } - if (fs.existsSync(projDir)) { - - // On vérifie que l'application peut lire les fichiers du dossier - fs.readdirSync(projDir).forEach(projection => { - - let projectionFile = ""; - - try { - projectionFile = projDir + "/" + projection; - fs.accessSync(projectionFile, fs.constants.R_OK); - } catch (err) { - LOGGER.error("Le fichier de projection ne peut etre lu: " + projectionFile); - } + } - try { - // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard - JSON.parse(fs.readFileSync(projectionFile)); - } catch (error) { - LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de projection: " + projectionFile); - LOGGER.error(error); - return false; - } - }); + } - } else { - LOGGER.fatal("Mauvaise configuration: Dossier de projections inexistant : " + projDir); - return false; - } + // Nettoyage de tous les managers - } + LOGGER.debug("Nettoyage des managers"); - } + this._projectionManager.flushCheckedProjection(); + this._baseManager.flushCheckedBaseConfiguration(); + this._operationManager.flushCheckedOperation(); + this._resourceManager.flushCheckedResource(); + this._sourceManager.flushCheckedSource(); + this._topologyManager.flushCheckedTopology(); + this._serverManager.flushCheckedServer(); LOGGER.info("Verification terminee."); - this._configuration = userConfiguration; - // On stocke le chemin absolu car c'est utile pour la suite, notamment pour les chemins relatifs que l'on aura - this._configurationPath = userConfigurationPath; + return true; } - /** * * @function - * @name loadOperations - * @description Chargement des opérations - * @param {string} operationsDirectory - Dossier contenant les opérations à charger (chemin absolu) - * @param {string} parametersDirectory - Dossier contenant les paramètres à charger (chemin absolu) + * @name saveServiceConfiguration + * @description Sauvegarde de la configuration du service + * @param {json} configuration - Configuration du service (contenu du service.json) + * @param {string} configurationPath - Chemin de la configuration du service (chemin du service.json) + * @param {json} logConfiguration - Configuration des logs du service (contenu du log4js.json) * */ + saveServiceConfiguration(configuration, configurationPath, logConfiguration) { - loadOperations(operationsDirectory, parametersDirectory) { - - LOGGER.info("Chargement des operations..."); - - if (!operationsDirectory) { - try { - operationsDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.operations.directory); - } catch (error) { - LOGGER.error("Impossible d'avoir le chemin aboslu du dossier des operations: " + this._configuration.application.operations.directory); - LOGGER.error(error); - return false; - } - } - - if (!parametersDirectory) { - try { - parametersDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.operations.parameters.directory); - } catch (error) { - LOGGER.error("Impossible d'avoir le chemin aboslu du dossier des parametres: " + this._configuration.application.operations.parameters.directory); - LOGGER.error(error); - return false; - } - } - - if (!this._operationManager.loadOperationDirectory(operationsDirectory, parametersDirectory)) { - LOGGER.error("Erreur lors du chargement des operations."); - return false; - } - - return true; + this._configuration = configuration; + this._configurationPath = configurationPath; + this._logConfiguration = logConfiguration; } /** * * @function - * @name loadProjections - * @description Chargement des opérations - * @param {string} projectionsDirectory - Dossier contenant les projections à charger (chemin absolu) + * @name loadServiceConfiguration + * @description Chargement de la configuration du service * */ + loadServiceConfiguration() { - loadProjections(projectionsDirectory) { + LOGGER.info("Chargement de la configuration du service..."); - LOGGER.info("Chargement des projections..."); - - if (!projectionsDirectory) { - try { - projectionsDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.projections.directory); - } catch (error) { - LOGGER.error("Impossible d'avoir le chemin aboslu du dossier des projections: " + this._configuration.application.projections.directory); - LOGGER.error(error); - return false; - } + // Chargement des opérations + LOGGER.info("Chargement des operations..."); + + let parametersDirectory = ""; + try { + parametersDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.operations.parameters.directory); + } catch (error) { + LOGGER.error("Impossible d'avoir le chemin aboslu du dossier des parametres: " + this._configuration.application.operations.parameters.directory); + LOGGER.error(error); + return false; } - if (!this._projectionManager.loadProjectionDirectory(projectionsDirectory)) { - LOGGER.error("Erreur lors du chargement des projections."); + if (!this._operationManager.loadParameterDirectory(parametersDirectory)) { + LOGGER.error("Erreur lors du chargement des operations."); return false; } - return true; - - } - - /** - * - * @function - * @name loadResources - * @description Chargement des ressources - * @param {table} userResourceDirectories - Tableau de dossiers contenant les ressources à charger (chemins absolus) - * - */ - - async loadResources(userResourceDirectories) { - - LOGGER.info("Chargement des ressources..."); - - // Nombre de ressources chargées - let loadedResources = 0; - - if (!userResourceDirectories) { - userResourceDirectories = this._configuration.application.resources.directories; + let operationsDirectory = ""; + try { + operationsDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.operations.directory); + } catch (error) { + LOGGER.error("Impossible d'avoir le chemin aboslu du dossier des operations: " + this._configuration.application.operations.directory); + LOGGER.error(error); + return false; } - if (!Array.isArray(userResourceDirectories)) { - LOGGER.error("La variable contenant les dossiers de ressources n'est pas un tableau"); + if (!this._operationManager.loadOperationDirectory(operationsDirectory)) { + LOGGER.error("Erreur lors du chargement des operations."); return false; } - if (userResourceDirectories.length === 0) { - LOGGER.error("Le tableau contenant les dossiers de ressources est vide"); + // Chargement des projections + LOGGER.info("Chargement des projections..."); + + let projectionsDirectory = ""; + try { + projectionsDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.projections.directory); + } catch (error) { + LOGGER.error("Impossible d'avoir le chemin aboslu du dossier des projections: " + this._configuration.application.projections.directory); + LOGGER.error(error); + return false; + } + + if (!this._projectionManager.loadProjectionDirectory(projectionsDirectory)) { + LOGGER.error("Erreur lors du chargement des projections."); return false; } - for (let i = 0; i < userResourceDirectories.length; i++) { + // Chargement des ressources + LOGGER.info("Chargement des ressources..."); + + for (let i = 0; i < this._configuration.application.resources.directories.length; i++) { let resourceDirectory = ""; try { - resourceDirectory = path.resolve(path.dirname(this._configurationPath), userResourceDirectories[i]); + resourceDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.resources.directories[i]); + LOGGER.info("Dossier de ressources : " + resourceDirectory); } catch (error) { - LOGGER.error("Impossible d'obtenir le chemin absolu du dossier de ressources: " + userResourceDirectories[i]); + LOGGER.error("Impossible d'obtenir le chemin absolu du dossier de ressources: " + this._configuration.application.resources.directories[i]); LOGGER.error(error); - return false; + continue; } - // Pour chaque fichier du dossier des ressources, on crée une ressource - const files = fs.readdirSync(resourceDirectory).filter( (file) => { - return path.extname(file).toLowerCase() === ".resource"; - }) - for (let fileName of files){ - - let resourceFile = resourceDirectory + "/" + fileName; - LOGGER.info("Chargement de: " + resourceFile); - - // Récupération du contenu en objet pour vérification puis création de la ressource - try { - - let resourceContent = JSON.parse(fs.readFileSync(resourceFile)); - // Vérification du contenu - let resourceChecked = await this._resourceManager.checkResource(resourceContent, this._sourceManager, this._operationManager, this._topologyManager) - if (!resourceChecked) { - LOGGER.error("Erreur lors du chargement de la ressource: " + resourceFile); - } else { - // Création de la ressource - this._resourceCatalog[resourceContent.resource.id] = this._resourceManager.createResource(resourceContent, this._operationManager); - loadedResources++; - } - - } catch (error) { - LOGGER.error(error); - LOGGER.error("Erreur lors de la lecture de la ressource: " + resourceFile); - } - - }; + if (!this._resourceManager.loadResourceDirectory(resourceDirectory)) { + LOGGER.error("Impossible de charger correctement le dossier de ressources " + resourceDirectory); + } else { + // On va continuer + LOGGER.info("Les ressources du dossier " + resourceDirectory + " sont chargées dans la mesure du possible") + } } - if (loadedResources === 0) { + if (this._resourceManager.resource.length === 0) { LOGGER.fatal("Aucune ressource n'a pu etre chargee"); return false; } - return true; - - } - - /** - * - * @function - * @name loadTopologies - * @description Chargement des topologies - * - */ - - loadTopologies() { - - LOGGER.info("Chargement des topologies..."); - - if (!this._topologyManager.loadAllTopologies()) { - LOGGER.error("Echec lors du chargement des topologies."); - return false; - } - - LOGGER.info("Topologies chargees."); - return true; - } - - /** - * - * @function - * @name loadSources - * @description Chargement des sources - * - */ - - async loadSources() { - - LOGGER.info("Chargement des sources..."); - - let loadedSources = 0; - - // On récupère les informations du resourceManager pour les intégrer au sourceManager du service - let listOfSourceIds = this._sourceManager.listOfSourceIds; - let sourceDescriptions = this._sourceManager.sourceDescriptions; - - // On va créer chaque source - if (listOfSourceIds.length !== 0) { - // On va charger chaque source identifiée - let sourceToRemove = new Array(); - for (let i = 0; i < listOfSourceIds.length; i++) { - - let sourceId = listOfSourceIds[i]; - LOGGER.info("Chargement de la source: " + sourceId); - - // On récupère la bonne topologie - let topologyId = this._sourceManager.getSourceTopology(sourceId); - if (topologyId === "") { - LOGGER.error("Erreur lors de la recuperation de l'id de la topologie associee a la source"); - throw errorManager.createError("Topology Id not found"); - } - - let topology = this._topologyManager.getTopologyById(topologyId); - - try { - assert.notDeepStrictEqual(topology, {}); - } catch(err) { - LOGGER.error("Erreur lors de la recuperation de la topologie associee a la source"); - LOGGER.error(err); - throw errorManager.createError("Topology not found"); - } - - // On crée la source - // TODO: a revoir -> cas ou plusieurs datasources utilisant des topologies différentes - let currentSource = this._sourceManager.createSource(sourceDescriptions[sourceId], topology); - - // On vérifie que le source peut bien être chargée ou connectée - try { - await this._sourceManager.connectSource(currentSource); - this._sourceCatalog[sourceId] = currentSource; - loadedSources++; - } catch (err) { - // on n'a pas pu se connecter à la source - // si une source ne peut être chargée alors on la supprime - LOGGER.error("Impossible de se connecter a la source: " + sourceId); - LOGGER.debug("Erreur : " + err); - LOGGER.warn("Suppression des references a la source dans les ressources qui l'utilisent..."); - let resourceList = this._sourceManager.listOfUsage(sourceId); - if (resourceList.length !== 0) { - for (let k = 0; k < resourceList.length; k++) { - LOGGER.warn("Suppression au sein de la ressource " + resourceList[k]); - let resource = this.resourceCatalog[resourceList[k]]; - resource.removeSource(sourceId); - } - } - sourceToRemove.push(sourceId); - - } - } - - // On supprime les sources qui n'ont pas pu être chargées - if (sourceToRemove.length !== 0) { - for (let k= 0; k < sourceToRemove.length; k++) { - LOGGER.warn("Suppression de la source " + sourceToRemove[k]); - this._sourceManager.removeSource(sourceToRemove[k]); - } - } - - } else { - LOGGER.fatal("Il n'y a aucune source a charger."); - throw errorManager.createError("No source found"); - } - - if (loadedSources === 0) { - LOGGER.fatal("Aucune source n'a pu etre chargee"); - throw errorManager.createError("No source loaded"); - } - } - - /** - * - * @function - * @name createServer - * @description Création du serveur - * @param {string} userApiDirectory - Dossier contenant les apis à charger sur ce serveur - * @param {string} userServerPrefix - Prefixe à utiliser sur le serveur créer - * - */ - - createServer(userApiDirectory, userServerPrefix) { - - LOGGER.info("Creation de l'application web..."); + // Chargement des serveurs + LOGGER.info("Creation de l'application ExpressJS..."); // Application Express let road2 = express(); @@ -892,6 +723,8 @@ module.exports = class Service { road2.set("service", this); // Initialisation des CORS + LOGGER.info("Initialisation des cors..."); + let corsConfiguration = {}; if (this._configuration.application.network.cors) { try { @@ -934,44 +767,94 @@ module.exports = class Service { return false; } + // Chargement des APIs indiquées dans la conf + LOGGER.info("Chargement des APIs indiquées dans la configuration..."); - - - // Chargement des APIs - if (!this._apisManager.loadAPISDirectory(road2, userApiDirectory, userServerPrefix)) { - LOGGER.error("Erreur lors du chargement des apis."); - return false; + for (let i = 0; i < this._configuration.application.apis.length; i++) { + let apiConfiguration = this._configuration.application.apis[i]; + if (!this._apisManager.loadApiConfiguration(road2, apiConfiguration)) { + LOGGER.fatal("Impossible de créer l'API " + apiConfiguration.name + "/" + apiConfiguration.version); + return false; + } } road2.all('/', (req, res) => { res.send('Road2'); }); - // Création des serveurs - if (!this._serverManager.createAllServer(road2)) { - LOGGER.fatal("Impossible de creer les serveurs."); + // Création des serveurs indiqués dans la conf + LOGGER.info("Chargement des serveurs..."); + + for (let i = 0; i < this._configuration.application.network.servers.length; i++) { + + let serverConf = this._configuration.application.network.servers[i]; + LOGGER.info("Chargement du serveur : " + serverConf.id); + + if (!this._serverManager.loadServerConfiguration(road2, serverConf)) { + LOGGER.fatal("Impossible de creer le serveur"); + return false; + } else { + LOGGER.info("Serveur chargé"); + } + } + + return true; + + } + + /** + * + * @function + * @name connectSources + * @description Connecter toutes les sources du service + * + */ + async connectSources() { + + LOGGER.info("Connexion des sources du service..."); + + // Connexion des sources + if (!(await this._sourceManager.connectAllSources())) { + LOGGER.fatal("Impossible de connecter toutes les sources du service"); return false; + } else { + LOGGER.info("Les sources du service potentiellement connectables ont été connectées"); + return true; } + } + + /** + * + * @function + * @name startServers + * @description Démarrage des serveurs du service + * + */ + startServers() { + + LOGGER.info("Démarrage des serveurs du service..."); + // Démarrage des serveurs - if (!this._serverManager.startAllServer()) { - LOGGER.fatal("Impossible de demarer les serveurs."); + if (!this._serverManager.startAllServers()) { + LOGGER.fatal("Impossible de démarrer tous les serveurs."); return false; + } else { + LOGGER.info("Les serveurs du service ont été démarrés"); + return true; } - return true; - } /** * * @function - * @name stopServer + * @name stopServers * @description Arrêt du serveur * */ - stopServer() { + stopServers() { // Extinction des serveurs if (!this._serverManager.stopAllServer()) { @@ -1000,8 +883,8 @@ module.exports = class Service { // --- // L'id est dans la requête let resourceId = request.resource; - // La ressource est dans le catalogue du service - let resource = this._resourceCatalog[resourceId]; + // La ressource est dans le catalogue du resourceManager + let resource = this._resourceManager.resource[resourceId]; // --- // Récupération de la source concernée par la requête @@ -1009,9 +892,8 @@ module.exports = class Service { // L'id est donné par le ressource let sourceId = resource.getSourceIdFromRequest(request); - // La source est dans le catalogue du service - // TODO : vérifier que la source existe toujours - let source = this._sourceCatalog[sourceId]; + // La source est dans le catalogue du sourceManager + let source = this._sourceManager.source[sourceId]; // --- //On renvoie la requête vers le moteur @@ -1026,24 +908,4 @@ module.exports = class Service { } - /** - * - * @function - * @name disconnectAllSources - * @description Fonction utilisée pour déconnecter toutes les sources. - * @param {Source} source - Objet Source ou hérité de la classe Source - * - */ - async disconnectAllSources() { - LOGGER.info("Déconnection de toutes les sources"); - try { - for (let source_id in this._sourceCatalog) { - await this._sourceManager.disconnectSource(this._sourceCatalog[source_id]); - } - } catch (err) { - LOGGER.error("Impossible de déconnecter la source.", err); - } - } - - } diff --git a/src/js/service/serviceAdministered.js b/src/js/service/serviceAdministered.js new file mode 100644 index 0000000..fdeac44 --- /dev/null +++ b/src/js/service/serviceAdministered.js @@ -0,0 +1,55 @@ +'use strict'; + +/** +* +* @class +* @name ServiceAdministered +* @description Classe modélisant une service administré par l'administrateur. +* @param {string} id - Identifiant du service administré +* @param {string} type - Type de service administré +* +*/ + +module.exports = class ServiceAdministered { + + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe ServiceAdministered + * + */ + constructor(id, type) { + + // Id d'une ressource. Il doit être unique. + this._id = id; + + // Type de la ressource + this._type = type; + + } + + /** + * + * @function + * @name get id + * @description Récupérer l'id du service administré + * + */ + get id () { + return this._id; + } + + /** + * + * @function + * @name get type + * @description Récupérer le type du service administré + * + */ + get type () { + return this._type; + } + +} diff --git a/src/js/service/serviceManager.js b/src/js/service/serviceManager.js new file mode 100644 index 0000000..7a93be4 --- /dev/null +++ b/src/js/service/serviceManager.js @@ -0,0 +1,130 @@ +'use strict'; + +const log4js = require('log4js'); +const Service = require('./service'); +const ServiceProcess = require('./serviceProcess'); + +// Création du LOGGER +const LOGGER = log4js.getLogger("SERVICEMANAGER"); + +/** +* +* @class +* @name serviceManager +* @description Gestionnaire des services gérés par un administrateur +* +*/ + +module.exports = class serviceManager { + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe serviceManager + * + */ + constructor() { + + // Catalogue de services chargés (objets de la classe ServiceAdministered) + this._loadedServiceAdministeredCatalog = {}; + + // Contenu de la configuration des services chargés (contenu du service.json) + this._loadedServiceConfigurations = {}; + + // Emplacement de la configuration des services chargés (chemin du service.json) + this._loadedServiceConfLocations = {}; + + } + + /** + * + * @function + * @name checkServiceConfiguration + * @description Vérification du contenu de la configuration d'un service (contenu du service.json) + * @param {object} configuration - Configuration du service (contenu du service.json) + * @param {string} configurationLocation - Chemin de la configuration du service (chemin du service.json) + * + */ + async checkServiceConfiguration(configuration, configurationLocation) { + + LOGGER.info("Vérification du contenu de la configuration du service..."); + + // Création d'un service pour vérifier la configuration + // On procède ainsi pour ne pas dupliquer du code déjà présent dans la classe Service + // De plus, on souhaite que la classe Service soit indépendante, donc ait le code de vérification de sa conf + let service = new Service(); + + if (!(await service.checkServiceConfiguration(configuration, configurationLocation))) { + LOGGER.error("La configuration du service n'est pas correcte"); + return false; + } else { + LOGGER.info("La configuration du service est correcte"); + return true; + } + + } + + /** + * + * @function + * @name loadService + * @description Création et lancement d'un service à partir de sa configuration + * @param {string} creationType - Type de création pour le service. Permet d'indiquer si on est dans le même process ou pas, voir sur une autre machine en théorie. + * @param {string} id - Id du service pour l'administrateur + * @param {string} configurationLocation - Emplacement de la configuration du service à charger + * @param {object} serviceConfiguration - Contenu de la configuration du service à charger + * + */ + loadService(creationType, id, configurationLocation, serviceConfiguration) { + + LOGGER.info("Création et lancement du service " + id); + + // TODO : voir s'il n'existe pas déjà ? + + let serviceAdministered = {}; + + // En fonction du type demandé, on va créer différemment le service + // Dans la configuration, on a "sameProcess", "newProcess" et "findByURI" + if (creationType === "sameProcess") { + + // Instanciation du service directement sur le même process que l'administrateur + LOGGER.error("Type de création non implémentée. Impossible de créer le service."); + return false; + + } else if (creationType === "newProcess") { + + LOGGER.debug("newProcess : Création d'un service dans un autre processus"); + + serviceAdministered = new ServiceProcess(id, configurationLocation); + + } else if (creationType === "findByURI") { + + LOGGER.error("Type de création non implémentée. Impossible de créer le service."); + return false; + + } else { + + // Cela ne devrait pas arriver + LOGGER.error("Le type de creation du service n'est pas valide. Impossible de créer un service"); + return false; + + } + + LOGGER.info("Service créé " + id); + + // On le démarre par le même appel, qu'importe son type de création + LOGGER.info("Démarrage du service " + id); + serviceAdministered.loadService(); + + // Sauvegarde du service dans le manager + LOGGER.info("Service démarré. Sauvegarde dans le serviceManager..."); + this._loadedServiceAdministeredCatalog[id] = serviceAdministered; + this._loadedServiceConfigurations[id] = serviceConfiguration; + this._loadedServiceConfLocations[id] = configurationLocation; + + return true; + + } + +} \ No newline at end of file diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js new file mode 100644 index 0000000..64d4e79 --- /dev/null +++ b/src/js/service/serviceProcess.js @@ -0,0 +1,118 @@ +'use strict'; + +const log4js = require('log4js'); +const { fork } = require('child_process'); + +const ServiceAdministered = require('./serviceAdministered'); + +// Création du LOGGER +const LOGGER = log4js.getLogger("SERVICEPRO"); + +/** +* +* @class +* @name ServiceProcess +* @description Classe modélisant un service administré mais qui se situe dans un autre processus que celui de l'administrateur qui le gère. +* +*/ + +module.exports = class ServiceProcess extends ServiceAdministered { + + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe ServiceProcess + * @param {string} id - Identifiant du service pour l'administrateur + * @param {string} location - Localisation de la configuration du service + * + */ + constructor(id, location) { + + // Constructeur parent + super(id, "newProcess"); + + // Emplacement de la configuration + this._configurationLocation = location; + + // Stockage de la configuration + this._configuration = {}; + + // Instance de childProcess quand le processus est lancé + this._serviceAdministered; + + } + + /** + * + * @function + * @name get configurationLocation + * @description Récupérer la configurationLocation du service + * + */ + get configurationLocation () { + return this._configurationLocation; + } + + /** + * + * @function + * @name get configuration + * @description Récupérer la configuration du service + * + */ + get configuration () { + return this._configuration; + } + + /** + * + * @function + * @name loadService + * @description Créer un service administré via un fork de processus + * + */ + loadService() { + + LOGGER.info("Création et lancement d'un service dans un autre processus"); + + // Un minimum de vérifications au cas où + if (typeof(this._configurationLocation) !== "string") { + LOGGER.error("Le chemin fourni n'est pas une string"); + return null; + } else { + LOGGER.debug("Le chemin fourni est bien une string"); + } + + if (this._configurationLocation === "") { + LOGGER.error("Le chemin fourni est vide"); + return null; + } else { + LOGGER.debug("Le chemin est renseigné : " + this._configurationLocation); + } + + // On prépare les options + let serviceOptions = {}; + + if (process.env.NODE_ENV === "debug") { + serviceOptions.execArgv = new Array(); + serviceOptions.execArgv.push("--inspect=0.0.0.0:9230"); + LOGGER.debug("Options du service : "); + LOGGER.debug(serviceOptions); + } else { + LOGGER.info("Pas d'options liées au debug"); + } + + // Création du service via un fork : on lance simplement service/main.js + LOGGER.info("Fork du processus pour créer le service..."); + + this._serviceAdministered = fork("./src/js/service/main.js", [this._configurationLocation], serviceOptions); + + LOGGER.info("Processus enfant créé"); + + return true; + + } + +} diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index bd9ae26..ea39ec5 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -39,7 +39,7 @@ module.exports = class osrmSource extends Source { * @name constructor * @description Constructeur de la classe osrmSource * @param {json} sourceJsonObject - Description de la source en json - * @param{topology} topology - Instance de la classe Topology + * @param {topology} topology - Instance de la classe Topology * */ constructor(sourceJsonObject, topology) { @@ -47,9 +47,6 @@ module.exports = class osrmSource extends Source { // Constructeur parent super(sourceJsonObject.id,sourceJsonObject.type, topology); - // Ajout des opérations possibles sur ce type de source - this.availableOperations.push("route"); - // Stockage de la configuration this._configuration = sourceJsonObject; diff --git a/src/js/sources/pgrSource.js b/src/js/sources/pgrSource.js index 08a7aae..1f1a68e 100644 --- a/src/js/sources/pgrSource.js +++ b/src/js/sources/pgrSource.js @@ -43,11 +43,7 @@ module.exports = class pgrSource extends Source { // Constructeur parent super(sourceJsonObject.id, "pgr", topology); - - // Ajout des opérations possibles sur ce type de source - this.availableOperations.push("route"); - this.availableOperations.push("isochrone"); - + // Stockage de la configuration this._configuration = sourceJsonObject; diff --git a/src/js/sources/smartroutingSource.js b/src/js/sources/smartroutingSource.js index 8d6d9d6..1fd701f 100644 --- a/src/js/sources/smartroutingSource.js +++ b/src/js/sources/smartroutingSource.js @@ -44,10 +44,6 @@ module.exports = class smartroutingSource extends Source { // Constructeur parent super(sourceJsonObject.id,sourceJsonObject.type, undefined); - // Ajout des opérations possibles sur ce type de source - this.availableOperations.push("route"); - this.availableOperations.push("isochrone"); - // Stockage de la configuration this._configuration = sourceJsonObject; diff --git a/src/js/sources/source.js b/src/js/sources/source.js index 72fa2e9..79c22ad 100644 --- a/src/js/sources/source.js +++ b/src/js/sources/source.js @@ -32,9 +32,6 @@ module.exports = class Source { // État de la connexion de la source this._connected = false; - // Liste d'opérations possibles sur la source - this._availableOperations = new Array(); - // Topologie dont dérive la source this._topology = topology; @@ -113,17 +110,6 @@ module.exports = class Source { this._state = st; } - /** - * - * @function - * @name get availableOperations - * @description Récupérer la liste des opérations possibles sur la source - * - */ - get availableOperations () { - return this._availableOperations; - } - /** * * @function @@ -135,23 +121,6 @@ module.exports = class Source { return this._topology; } - /** - * - * @function - * @name isOperationAvailable - * @description Savoir si une opération est disponible sur la source - * @param {string} operationId - Id de l'opération - * - */ - isOperationAvailable (operationId) { - for (let i = 0; i < this._availableOperations.length; i++ ) { - if (this._availableOperations[i] === operationId) { - return true; - } - } - return false; - } - /** * * @function diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index 10337b2..83fe2a2 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -22,203 +22,100 @@ module.exports = class sourceManager { */ constructor() { - // Liste des ids des sources gérées par le manager - this._listOfSourceIds = new Array(); + // Liste des ids des sources chargées par le manager + this._loadedSourceId = new Array(); - // Description des sources conservées de manière unique dans _listOfSourceIds - this._sourceDescriptions = {}; + // Liste des ids des sources vérifiées par le manager + this._checkedSourceId = new Array(); - // Correspondance entre l'id d'une source et l'id de la topologie dont elle dérive - this._sourceTopology = {}; + // Catalogue des sources du manager + this._source = {}; - // Correspondance entre l'id d'une source et l'id des ressources où elle est utilisée - this._sourceUsage = {}; + // Descriptions des sources chargées par le manager + this._loadedSourceConfiguration = {}; - } + // Descriptions des sources vérifiées par le manager + this._checkedSourceConfiguration = {}; - /** - * - * @function - * @name get listOfSourceIds - * @description Récupérer l'ensemble des ids de sources - * - */ - get listOfSourceIds() { - return this._listOfSourceIds; - } + // Correspondance entre l'id d'une source et l'id de la topologie dont elle dérive + this._sourceTopology = {}; - /** - * - * @function - * @name get sourceTopology - * @description Récupérer la correspondance source/topologie - * - */ - get sourceTopology() { - return this._sourceTopology; - } + // Correspondance entre les sources et les opérations possibles + this._operationsByType = { + "osrm": ["nearest", "route"], + "pgr": ["route", "isochrone"], + "smartrouting": ["route", "isochrone"] + }; - /** - * - * @function - * @name get sourceDescriptions - * @description Récupérer l'ensemble des descriptions des sources conservées - * - */ - get sourceDescriptions() { - return this._sourceDescriptions; } /** * * @function - * @name get sourceUsage - * @description Récupérer l'ensemble des correspondances de sources + * @name get loadedSourceId + * @description Récupérer l'ensemble des ids de sources chargées * */ - get sourceUsage() { - return this._sourceUsage; + get loadedSourceId() { + return this._loadedSourceId; } /** * * @function - * @name listOfUsage - * @description Récupérer l'ensemble des id de ressources utilisant une source - * @param {string} id - Id de la source - * @return {table} Liste des id de ressource + * @name get source + * @description Récupérer l'ensemble des ids de sources chargées * */ - listOfUsage(id) { - return this._sourceUsage[id]; + get source() { + return this._source; } /** * * @function - * @name removeSource - * @description Supprimer une source - * @param {string} id - Id de la source - * @return {boolean} + * @name get sourceTopology + * @description Récupérer la correspondance source/topologie * */ - removeSource(id) { - - let index = this._listOfSourceIds.indexOf(id); - if (index !== -1) { - this._listOfSourceIds.splice(index,1); - } else { - return false; - } - - if (!delete this._sourceDescriptions[id]) { - return false; - } - if (!delete this._sourceTopology[id]) { - return false; - } - if (!delete this._sourceUsage[id]) { - return false; - } - - return true; + get sourceTopology() { + return this._sourceTopology; } - /** + /** * * @function - * @name addUsage - * @description Ajouter l'usage, via l'id, d'une ressource pour une source - * @param {string} sourceId - Id de la source - * @param {string} resourceId - Id de la ressource - * @return {boolean} + * @name get operationsByType + * @description Récupérer l'ensemble des opérations possibles par type de source * */ - addUsage(sourceId, resourceId) { - if (!this._sourceUsage[sourceId]) { - this._sourceUsage[sourceId] = new Array(); - } - if (!Array.isArray(this._sourceUsage[sourceId])) { - return false; - } - this._sourceUsage[sourceId].push(resourceId); - return true; + get operationsByType() { + return this._operationsByType; } /** * * @function - * @name set listOfSourceIds + * @name set loadedSourceId * @description Attribuer l'ensemble des ids de sources * @param {table} list - État de la connexion * */ - set listOfSourceIds(list) { - this._listOfSourceIds = list; - } - - /** - * - * @function - * @name set sourceDescriptions - * @description Attribuer l'ensemble des descriptions des sources conservées - * @param {object} descriptions - Objet contenant les descriptions - * - */ - set sourceDescriptions(descriptions) { - this._sourceDescriptions = descriptions; - } - - /** - * - * @function - * @name getSourceDescriptionById - * @description Récupérer la description de la source indiquée par son id - * @param {string} id - Id de la source - * - */ - getSourceDescriptionById(id) { - if (this._sourceDescriptions[id]) { - return this._sourceDescriptions[id]; - } else { - return {}; - } - - } - - /** - * - * @function - * @name getSourceTopology - * @description Récupérer l'id de la topologie dont dérive une source - * @param {string} sourceId - Id de la source - * @return {string} Id d'une topologie - * - */ - - getSourceTopology(sourceId) { - - if(this._sourceTopology[sourceId]) { - return this._sourceTopology[sourceId]; - } else { - return ""; - } - + set loadedSourceId(list) { + this._loadedSourceId = list; } /** * * @function - * @name checkSource + * @name checkSourceConfiguration * @description Fonction utilisée pour vérifier la partie source d'un fichier de description d'une ressource. * @param {json} sourceJsonObject - Description JSON de la source - * @param {object} operationManager - Le manager des opérations du service - * @param {table} resourceOperationTable - Tableau contenant l'ensemble des id d'opérations disponibles pour cette ressource - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @return {boolean} * */ - checkSource(sourceJsonObject, operationManager, resourceOperationTable) { + checkSourceConfiguration(sourceJsonObject) { LOGGER.info("Verification de la source..."); @@ -227,17 +124,37 @@ module.exports = class sourceManager { LOGGER.error("La ressource contient une source sans id."); return false; } else { - // On vérifie que l'id n'est pas déjà pris. - if (this._listOfSourceIds.length !== 0) { + // On vérifie que l'id n'est pas déjà chargé. + if (this._loadedSourceId.length !== 0) { - let present = false; + for (let i = 0; i < this._loadedSourceId.length; i++ ) { + if (this._loadedSourceId[i] === sourceJsonObject.id) { + LOGGER.info("La source contenant l'id " + sourceJsonObject.id + " est deja chargée."); + // On vérifie que la source décrite et celle déjà identifiée soient exactement les mêmes + if (this.checkDuplicationLoadedSource(sourceJsonObject)) { + break; + } else { + LOGGER.error("La source contenant l'id " + sourceJsonObject.id + " n'est pas identique à la source deja identifiee."); + return false; + } + + } else { + // on continue la boucle de vérification + } + } - for (let i = 0; i < this._listOfSourceIds.length; i++ ) { - if (this._listOfSourceIds[i] === sourceJsonObject.id) { - LOGGER.info("La source contenant l'id " + sourceJsonObject.id + " est deja referencee."); + } else { + // C'est la première source. + } + + // On vérifie que l'id n'est pas déjà vérifié. + if (this._checkedSourceId.length !== 0) { + + for (let i = 0; i < this._checkedSourceId.length; i++ ) { + if (this._checkedSourceId[i] === sourceJsonObject.id) { + LOGGER.info("La source contenant l'id " + sourceJsonObject.id + " est deja vérifiée."); // On vérifie que la source décrite et celle déjà identifiée soient exactement les mêmes - if (this.checkDuplicationSource(sourceJsonObject)) { - present = true; + if (this.checkDuplicationCheckedSource(sourceJsonObject)) { break; } else { LOGGER.error("La source contenant l'id " + sourceJsonObject.id + " n'est pas identique à la source deja identifiee."); @@ -252,6 +169,7 @@ module.exports = class sourceManager { } else { // C'est la première source. } + } // Type @@ -268,8 +186,6 @@ module.exports = class sourceManager { available = true; LOGGER.info("Source osrm."); - let operationFound = false; - // On vérifie que le module osrm est disponible try { let osrmTest = require('osrm'); @@ -278,28 +194,11 @@ module.exports = class sourceManager { return false; } - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("route")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("route", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - - if (!operationFound) { - LOGGER.error("Le service ne propose pas d'operations disponibles pour ce type de source (ex. route), il n'est donc pas possible de charger cette source."); - return false; - } - if (!this.checkSourceOsrm(sourceJsonObject)) { LOGGER.error("Erreur lors de la verification de la source osrm."); return false; } else { - // il n'y a eu aucun problème, la ressource est correctement configurée. + // il n'y a eu aucun problème, la source est correctement configurée. } } else { // On va voir si c'est un autre type. @@ -318,37 +217,6 @@ module.exports = class sourceManager { return false; } - let operationFound = false; - - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("route")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("route", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("isochrone")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("isochrone", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - - if (!operationFound) { - LOGGER.error("Le service ne propose pas d'operations disponibles pour ce type de source (ex. route, isochrone), il n'est donc pas possible de charger cette source."); - return false; - } - if (!this.checkSourcePgr(sourceJsonObject)) { LOGGER.error("Erreur lors de la verification de la source pgr."); return false; @@ -364,37 +232,6 @@ module.exports = class sourceManager { available = true; LOGGER.info("Source smartrouting."); - let operationFound = false; - - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("route")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("route", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("isochrone")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("isochrone", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - - if (!operationFound) { - LOGGER.error("Le service ne propose pas d'operations disponibles pour ce type de source (ex. route, isochrone), il n'est donc pas possible de charger cette source."); - return false; - } - if (!this.checkSourceSmartrouting(sourceJsonObject)) { LOGGER.error("Erreur lors de la verification de la source smartrouting."); return false; @@ -413,9 +250,6 @@ module.exports = class sourceManager { } } - this._listOfSourceIds.push(sourceJsonObject.id); - this._sourceDescriptions[sourceJsonObject.id] = sourceJsonObject; - LOGGER.info("Fin de la verification de la source."); return true; @@ -592,19 +426,50 @@ module.exports = class sourceManager { /** * * @function - * @name checkDuplicationSource + * @name checkDuplicationLoadedSource * @description Fonction utilisée pour vérifier que le contenu d'un fichier de description d'une source est bien le même qu'un autre. * @param {json} sourceJsonObject - Description JSON de la source - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @return {boolean} + * + */ + + checkDuplicationLoadedSource(sourceJsonObject) { + + LOGGER.info("Comparaison des deux sources identifiees et devant etre identiques..."); + + // On récupère la description de la source faisant office de référence car lue la première. + let referenceSource = this._loadedSourceConfiguration[sourceJsonObject.id]; + + // On compare les deux objets + try { + assert.deepStrictEqual(sourceJsonObject, referenceSource); + } catch (err) { + LOGGER.error("Les deux sources ne sont pas identiques."); + LOGGER.debug(err); + return false; + } + + LOGGER.info("Les deux sources sont identiques."); + return true; + + } + + /** + * + * @function + * @name checkDuplicationCheckedSource + * @description Fonction utilisée pour vérifier que le contenu d'un fichier de description d'une source est bien le même qu'un autre. + * @param {json} sourceJsonObject - Description JSON de la source + * @return {boolean} * */ - checkDuplicationSource(sourceJsonObject) { + checkDuplicationCheckedSource(sourceJsonObject) { LOGGER.info("Comparaison des deux sources identifiees et devant etre identiques..."); // On récupère la description de la source faisant office de référence car lue la première. - let referenceSource = this._sourceDescriptions[sourceJsonObject.id]; + let referenceSource = this._checkedSourceConfiguration[sourceJsonObject.id]; // On compare les deux objets try { @@ -623,20 +488,54 @@ module.exports = class sourceManager { /** * * @function - * @name createSource + * @name saveCheckedSource + * @description Sauvegarder l'id de la source vérifié + * @param {object} configuration - Id de la source que l'on veut sauvegarder + * + */ + saveCheckedSource(configuration) { + + this._checkedSourceId.push(configuration.id); + this._checkedSourceConfiguration[configuration.id] = configuration; + + } + + /** + * + * @function + * @name flushCheckedSource + * @description Vider la liste des source déjà vérifiées + * + */ + flushCheckedSource() { + + this._checkedSourceId = new Array(); + this._checkedSourceConfiguration = {}; + + } + + /** + * + * @function + * @name loadSourceConfiguration * @description Fonction utilisée pour créer une source. * @param {json} sourceJsonObject - Description JSON de la source * @param {Topology} topology - Instance de la classe Topology - * @return {Source} Source créée + * @return {boolean} * */ - createSource(sourceJsonObject, topology) { + loadSourceConfiguration(sourceJsonObject, topology) { LOGGER.info("Creation de la source: " + sourceJsonObject.id); let source; + if (this._source[sourceJsonObject.id]) { + LOGGER.info("La source " + sourceJsonObject.id + " existe déjà"); + return true; + } + if (sourceJsonObject.type === "osrm") { source = new osrmSource(sourceJsonObject, topology); } else if (sourceJsonObject.type === "pgr") { @@ -646,47 +545,102 @@ module.exports = class sourceManager { source = new smartroutingSource(sourceJsonObject); } else { // On va voir si c'est un autre type. + LOGGER.error("Le type de la source est inconnu"); + return false; } - return source; + + this._loadedSourceId.push(sourceJsonObject.id); + this._loadedSourceConfiguration[sourceJsonObject.id] = sourceJsonObject; + this._source[sourceJsonObject.id] = source; + + return true; + } /** * * @function * @name connectSource - * @description Fonction utilisée pour connecter une source. - * @param {Source} source - Objet Source ou hérité de la classe Source + * @description Fonction utilisée pour connecter une source. + * On la sépare volontairement du load de la source car on veut pouvoir gérer ces actions de manière indépendante. + * @param {string} sourceId - Id de la source que l'on veut connecter * */ - async connectSource(source) { + async connectSource(sourceId) { + + LOGGER.info("Connexion a la source: " + sourceId); - LOGGER.info("Connexion a la source: " + source.id); try { - await source.connect(); + + await this._source[sourceId].connect(); LOGGER.info("Source connectee."); + return true; + } catch (err) { + LOGGER.error("Impossible de connecter la source.", err); - throw errorManager.createError("Impossible de connecter la source."); + return false; + } + } /** * * @function - * @name disconnectSource - * @description Fonction utilisée pour déconnecter une source. - * @param {Source} source - Objet Source ou hérité de la classe Source + * @name connectAllSources + * @description Connecter l'ensemble des sources disponibles dans le manager * */ - async disconnectSource(source) { - LOGGER.info("Déconnection de la source: " + source.id); + async connectAllSources() { + + LOGGER.info("Connexion de l'ensemble des sources..."); + + if (this._loadedSourceId.length === 0) { + LOGGER.error("Aucune source n'est disponible"); + return false; + } + try { - await source.disconnect(); - LOGGER.info("Source déconnectee."); + assert.deepStrictEqual(this._loadedSourceConfiguration, {}); + LOGGER.error("Aucune source n'a été préalablement chargée"); + return false; } catch (err) { - LOGGER.error("Impossible de déconnecter la source.", err); - throw errorManager.createError("Impossible de déconnecter la source."); + // tout va bien } + + let nbSourceConnected = 0; + + for (let i = 0; i < this._loadedSourceId.length; i++) { + + LOGGER.info("Source : " + this._loadedSourceId[i]); + + if (!(await this.connectSource(this._loadedSourceId[i]))) { + + LOGGER.error("Source " + this._loadedSourceId[i] + " non connectée"); + // TODO : on continue de connecter les autres sources et on gère après coup la MAJ des ressources, du getcap, etc... + // Pour le moment, s'il y a une source qui ne fonctionne pas, on arrête le serveur + return false; + + } else { + + LOGGER.info("Source " + this._loadedSourceId[i] + " connectée"); + nbSourceConnected++; + + } + + } + + LOGGER.info("Les démarrages se sont bien déroulés."); + + if (nbSourceConnected === 0) { + LOGGER.error("Aucune source n'a pu être connectée"); + return false; + } else { + LOGGER.info("Au moins une source a été connectée"); + return true; + } + } } diff --git a/src/js/topology/topologyManager.js b/src/js/topology/topologyManager.js index 77faeb5..0f0a5df 100644 --- a/src/js/topology/topologyManager.js +++ b/src/js/topology/topologyManager.js @@ -28,14 +28,20 @@ module.exports = class topologyManager { */ constructor(baseManager, projectionManager) { + // Liste des descriptions de topologies chargées par le manager + this._loadedTopologyId = new Array(); + // Liste des descriptions de topologies vérifiées et validées par le manager - this._listOfTopologyIds = new Array(); + this._checkedTopologyId = new Array(); + + // Descriptions des topologies chargées par le manager + this._loadedTopologyConfiguration = {}; - // Descriptions des topologies vérifiées et validées par le manager - this._topologyDescriptions = {}; + // Descriptions des topologies vérifiées par le manager + this._checkedTopologyConfiguration = {}; // Le catalogue des topologies créées par le manager - this._topologiesCatalog = {}; + this._topologyCatalog = {}; // Manager de bases this._baseManager = baseManager; @@ -48,25 +54,25 @@ module.exports = class topologyManager { /** * * @function - * @name get topologiesCatalog + * @name get topologyCatalog * @description Récupérer le catalogue des topologies * */ - get topologiesCatalog() { - return this._topologiesCatalog; + get topologyCatalog() { + return this._topologyCatalog; } /** * * @function - * @name getTopologyById + * @name getTopology * @description Récupérer une topologie via son id - * @param{string} id - Id de la topologie + * @param {string} id - Id de la topologie * */ - getTopologyById(id) { - if (this._topologiesCatalog[id]) { - return this._topologiesCatalog[id]; + getTopology(id) { + if (this._topologyCatalog[id]) { + return this._topologyCatalog[id]; } else { return {}; } @@ -76,26 +82,28 @@ module.exports = class topologyManager { /** * * @function - * @name checkTopology + * @name checkTopologyConfiguration * @description Vérification de la description d'une topologie * @param{json} topologyJsonDescription - JSON décrivant une topologie * */ - async checkTopology(topologyJsonDescription) { + async checkTopologyConfiguration(topologyJsonDescription) { // Id de la topologie if (!topologyJsonDescription.id) { LOGGER.error("La topologie ne contient pas d'id."); return false; } else { - // On vérifie que l'id n'est pas déjà pris. - if (this._listOfTopologyIds.length !== 0) { - - for (let i = 0; i < this._listOfTopologyIds.length; i++ ) { - if (this._listOfTopologyIds[i] === topologyJsonDescription.id) { - LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est deja referencee."); - // On vérifie que la source décrite et celle déjà identifiée soient exactement les mêmes - if (this.checkDuplicationTopology(topologyJsonDescription)) { + + // On vérifie que l'id n'est pas déjà chargés. + if (this._loadedTopologyId.length !== 0) { + + for (let i = 0; i < this._loadedTopologyId.length; i++ ) { + if (this._loadedTopologyId[i] === topologyJsonDescription.id) { + + LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est deja chargée."); + // On vérifie que la topology décrite et celle déjà identifiée soient exactement les mêmes + if (this.checkDuplicationLoadedTopology(topologyJsonDescription)) { LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est identique à la topologie deja identifiee."); return true; } else { @@ -103,14 +111,29 @@ module.exports = class topologyManager { return false; } - } else { - // on continue de vérifier } } - - } else { - // C'est la première source, on continue la vérification } + + // On vérifie que l'id n'est pas déjà vérifiés. + if (this._checkedTopologyId.length !== 0) { + + for (let i = 0; i < this._checkedTopologyId.length; i++ ) { + if (this._checkedTopologyId[i] === topologyJsonDescription.id) { + + LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est deja vérifiée."); + // On vérifie que la topology décrite et celle déjà identifiée soient exactement les mêmes + if (this.checkDuplicationCheckedTopology(topologyJsonDescription)) { + LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est identique à la topologie deja identifiee."); + return true; + } else { + LOGGER.error("La topologie contenant l'id " + topologyJsonDescription.id + " n'est pas identique à la topologie deja identifiee."); + return false; + } + + } + } + } } // Description de la topologie @@ -127,7 +150,7 @@ module.exports = class topologyManager { return false; } else { // Vérification de la projection - if (!this._projectionManager.isAvailableById(topologyJsonDescription.projection)) { + if (!this._projectionManager.isChecked(topologyJsonDescription.projection)) { LOGGER.error("La topologie indique une projection non disponible sur le service: " + topologyJsonDescription.projection); return false; } @@ -163,10 +186,6 @@ module.exports = class topologyManager { } - // on sauvegarde l'id de la topologie pour savoir qu'elle a déjà été vérifiée et que sa description est valide - this._listOfTopologyIds.push(topologyJsonDescription.id); - this._topologyDescriptions[topologyJsonDescription.id] = topologyJsonDescription; - return true; } @@ -224,9 +243,11 @@ module.exports = class topologyManager { LOGGER.error("La ressource ne contient pas de parametre 'topology.storage.dbConfig'."); return false; } else { - if (!(await this._baseManager.checkBase(topologyJsonDescription.storage.base.dbConfig))) { + if (!(await this._baseManager.checkBaseConfiguration(topologyJsonDescription.storage.base.dbConfig))) { LOGGER.error("La ressource contient un parametre 'topology.storage.dbConfig' incorrect."); return false; + } else { + this._baseManager.saveCheckedBaseConfiguration(topologyJsonDescription.storage.base.dbConfig); } } @@ -322,19 +343,19 @@ module.exports = class topologyManager { /** * * @function - * @name checkDuplicationTopology + * @name checkDuplicationLoadedTopology * @description Fonction utilisée pour vérifier que le contenu d'un fichier de description d'une source est bien le même qu'un autre. * @param {json} topologyJsonDescription - Description JSON de la topologie * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur * */ - checkDuplicationTopology(topologyJsonDescription) { + checkDuplicationLoadedTopology(topologyJsonDescription) { LOGGER.info("Comparaison des deux topologies identifiees et devant etre identiques..."); // On récupère la description de la topologie faisant office de référence car lue la première. - let referenceTopology = this._topologyDescriptions[topologyJsonDescription.id]; + let referenceTopology = this._loadedTopologyConfiguration[topologyJsonDescription.id]; // On compare les deux objets try { @@ -353,22 +374,83 @@ module.exports = class topologyManager { /** * * @function - * @name createTopology + * @name checkDuplicationCheckedTopology + * @description Fonction utilisée pour vérifier que le contenu d'un fichier de description d'une source est bien le même qu'un autre. + * @param {json} topologyJsonDescription - Description JSON de la topologie + * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * + */ + + checkDuplicationCheckedTopology(topologyJsonDescription) { + + LOGGER.info("Comparaison des deux topologies identifiees et devant etre identiques..."); + + // On récupère la description de la topologie faisant office de référence car lue la première. + let referenceTopology = this._checkedTopologyConfiguration[topologyJsonDescription.id]; + + // On compare les deux objets + try { + assert.deepStrictEqual(topologyJsonDescription, referenceTopology); + } catch (err) { + LOGGER.error("Les deux topologies ne sont pas identiques."); + LOGGER.error(err); + return false; + } + + LOGGER.info("Les deux topologies sont identiques."); + return true; + + } + + /** + * + * @function + * @name saveCheckedTopology + * @description Sauvegarder l'id de la topology vérifié + * @param {object} configuration - Id de la topology que l'on veut sauvegarder + * + */ + saveCheckedTopology(configuration) { + + this._checkedTopologyId.push(configuration.id); + this._checkedTopologyConfiguration[configuration.id] = configuration; + + } + + /** + * + * @function + * @name flushCheckedTopology + * @description Vider la liste des topology déjà vérifiées + * + */ + flushCheckedTopology() { + + this._checkedTopologyId = new Array(); + this._checkedTopologyConfiguration = {}; + + } + + /** + * + * @function + * @name loadTopologyConfiguration * @description Fonction utilisée pour créer une source. * @param {json} topologyJsonObject - Description JSON de la topologie * @return {Topology} Topologie créée - Instance d'une classe fille de Topology * */ - createTopology(topologyJsonObject) { + loadTopologyConfiguration(topologyJsonObject) { LOGGER.info("Creation de la topologie: " + topologyJsonObject.id); let topology; // on vérifie d'abord que la topologie n'a pas déjà été créée - if (this._topologiesCatalog[topologyJsonObject.id]) { - return this._topologiesCatalog[topologyJsonObject.id]; + if (this._topologyCatalog[topologyJsonObject.id]) { + LOGGER.info("La topologie " + topologyJsonObject.id + " existe déjà"); + return true; } else { // la topologie n'existe pas, on continue } @@ -388,7 +470,14 @@ module.exports = class topologyManager { } else if (topologyJsonObject.type === "db") { // récupération de la base - let base = this._baseManager.createBase(topologyJsonObject.storage.base.dbConfig); + let base = {}; + if (!this._baseManager.loadBaseConfiguration(topologyJsonObject.storage.base.dbConfig)) { + LOGGER.error("Impossible de charger la base configurée dans " + topologyJsonObject.storage.base.dbConfig); + return false; + } else { + base = this._baseManager.getBase(topologyJsonObject.storage.base.dbConfig); + } + // création des tableaux d'attributs let defaultAttributes = new Array(); let otherAttributes = new Array(); @@ -402,6 +491,7 @@ module.exports = class topologyManager { // cela ne doit pas arriver } } + // création de la topologie topology = new DbTopology(topologyJsonObject.id, topologyJsonObject.description, topologyJsonObject.projection, topologyJsonObject.bbox, base, topologyJsonObject.storage.base.schema, @@ -409,38 +499,14 @@ module.exports = class topologyManager { } else { // On va voir si c'est un autre type. - } - - this._topologiesCatalog[topologyJsonObject.id] = topology; - - return topology; - } - - /** - * - * @function - * @name loadAllTopologies - * @description Fonction utilisée pour créer une source. - * - */ - - loadAllTopologies() { - - if (this._listOfTopologyIds.length === 0) { - LOGGER.error("Aucune topologie a charger."); + LOGGER.error("Type de la topology inconnu"); return false; - } else { - // on continue } - for (let i = 0; i < this._listOfTopologyIds.length; i++) { - - let id = this._listOfTopologyIds[i]; - let configuration = this._topologyDescriptions[id]; - - this._topologiesCatalog[id] = this.createTopology(configuration); - - } + // on sauvegarde l'id de la topologie pour savoir qu'elle a déjà été vérifiée et que sa description est valide + this._loadedTopologyId.push(topologyJsonObject.id); + this._loadedTopologyConfiguration[topologyJsonObject.id] = topologyJsonObject; + this._topologyCatalog[topologyJsonObject.id] = topology; return true; diff --git a/src/js/utils/logManager.js b/src/js/utils/logManager.js new file mode 100644 index 0000000..c9bc972 --- /dev/null +++ b/src/js/utils/logManager.js @@ -0,0 +1,91 @@ +'use strict'; + +const log4js = require('log4js'); + +// Création du LOGGER +const LOGGER = log4js.getLogger("LOGMANAGER"); + +module.exports = { + + /** + * + * @function + * @name checkLogConfiguration + * @description Vérification de la configuration d'un logger + * @param {object} userLogConfiguration - Configuration du logger + * @param {string} userLogConfPath - Chemin absolu de la configuration du logger + * @return {boolean} + * + */ + + checkLogConfiguration: function(userLogConfiguration, userLogConfPath) { + + LOGGER.info("Vérification de la configuration du logger..."); + + if (!userLogConfiguration) { + + LOGGER.error("Aucune configuration pour les logs."); + return false; + + } else { + + if (!userLogConfiguration.mainConf) { + LOGGER.error("Mausvaise configuration pour les logs: 'mainConf' absent."); + return false; + } else { + + if (!userLogConfiguration.mainConf.appenders) { + LOGGER.error("Mausvaise configuration pour les logs: 'mainConf.appenders' absent."); + return false; + } + + if (!userLogConfiguration.mainConf.categories) { + LOGGER.error("Mausvaise configuration pour les logs: 'mainConf.categories' absent."); + return false; + } + + } + + if (!userLogConfiguration.httpConf) { + + LOGGER.error("Mausvaise configuration pour les logs: 'httpConf' absent."); + return false; + + } else { + + if (!userLogConfiguration.httpConf.level) { + LOGGER.error("Mausvaise configuration pour les logs: 'httpConf.level' absent."); + return false; + } else { + + if (typeof userLogConfiguration.httpConf.level !== "string") { + LOGGER.error("Mausvaise configuration pour les logs: 'httpConf.level' n'est pas une chaine de caracteres"); + return false; + } + + } + + if (!userLogConfiguration.httpConf.format) { + + LOGGER.error("Mausvaise configuration pour les logs: 'httpConf.format' absent."); + return false; + + } else { + + if (typeof userLogConfiguration.httpConf.format !== "string") { + LOGGER.error("Mausvaise configuration pour les logs: 'httpConf.format' n'est pas une chaine de caracteres"); + return false; + } + + } + + } + + } + + LOGGER.info("Vérification des logs terminée"); + return true; + + } + +} \ No newline at end of file diff --git a/src/js/utils/processManager.js b/src/js/utils/processManager.js index c7a5332..1141caf 100644 --- a/src/js/utils/processManager.js +++ b/src/js/utils/processManager.js @@ -18,11 +18,11 @@ module.exports = { shutdown: function(error) { - LOGGER.info("Extinction du serveur.") - - log4js.shutdown(function(){ - process.exit(error); - }); + LOGGER.info("Extinction du processus"); + // TODO : à voir si on en a besoin + // log4js.shutdown(() => {}); + // setTimeout(() => {process.exit(error);},"1000"); + process.exit(error); } diff --git a/test/functional/configuration/cucumber/features/conf-admin.feature b/test/functional/configuration/cucumber/features/conf-admin.feature new file mode 100644 index 0000000..5f885ab --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-admin.feature @@ -0,0 +1,351 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur admin +Feature: Road2 Admin configuration + Tests fonctionnels de Road2 sur la configuration du serveur admin + + Background: + Given I have loaded all my test configuration + + Scenario: [server.json] administration différent + Given a valid configuration + And with parameter "test" for attribute "administration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.logs' absent" + + Scenario: [server.json] administration vide + Given a valid configuration + And with parameter "" for attribute "administration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration' absent" + + Scenario: [server.json] administration absent + Given a valid configuration + And without attribute "administration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration' absent" + + Scenario: [server.json] administration.logs différent + Given a valid configuration + And with parameter "test" for attribute "administration.logs" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.logs.configuration' absent" + + Scenario: [server.json] administration.logs vide + Given a valid configuration + And with parameter "" for attribute "administration.logs" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.logs' absent" + + Scenario: [server.json] administration.logs absent + Given a valid configuration + And without attribute "administration.logs" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.logs' absent" + + Scenario: [server.json] administration.logs.configuration avec un fichier qui n'existe pas + Given a valid configuration + And with parameter "test" for attribute "administration.logs.configuration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: fichier de configuration des logs inexistant" + + Scenario: [server.json] administration.logs.configuration sur un fichier non lisible + Given a valid configuration + And a file "file.json" non readable + And with parameter "file.json" for attribute "administration.logs.configuration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs" + + Scenario: [server.json] administration.logs.configuration sur un fichier vide + Given a valid configuration + And a file "file.json" + And with parameter "file.json" for attribute "administration.logs.configuration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs" + + Scenario: [server.json] administration.logs.configuration sur un fichier incorrect + Given a valid configuration + And a wrong JSON file "file.json" + And with parameter "file.json" for attribute "administration.logs.configuration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs" + + Scenario: [server.json] administration.logs.configuration vide + Given a valid configuration + And with parameter "" for attribute "administration.logs.configuration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.logs.configuration' absent" + + Scenario: [server.json] administration.logs.configuration absent + Given a valid configuration + And without attribute "administration.logs.configuration" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.logs.configuration' absent" + + Scenario: [server.json] network different + Given a valid configuration + And with parameter "test" for attribute "administration.network" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.network.server' absent" + + Scenario: [server.json] network vide + Given a valid configuration + And with parameter "" for attribute "administration.network" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.network' absent" + + Scenario: [server.json] network absent + Given a valid configuration + And without attribute "administration.network" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.network' absent" + + Scenario: [server.json] network.server different + Given a valid configuration + And with parameter "test" for attribute "administration.network.server" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "La configuration du serveur n'indique aucun id" + + Scenario: [server.json] network.server vide + Given a valid configuration + And with parameter "" for attribute "administration.network.server" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.network.server' absent" + + Scenario: [server.json] network.server absent + Given a valid configuration + And without attribute "administration.network.server" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.network.server' absent" + + Scenario: [server.json] serveur http avec id different + Given a valid configuration + And with parameter "test" for attribute "administration.network.server.id" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [server.json] serveur http sans id + Given a valid configuration + And without attribute "administration.network.server.id" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "La configuration du serveur n'indique aucun id" + + Scenario: [server.json] serveur http avec un mauvais https + Given a valid configuration + And with parameter "test" for attribute "administration.network.server.https" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Le parametre https est mal renseigné. Valeurs disponibles: 'true' ou 'false'." + + Scenario: [server.json] serveur http sans https + Given a valid configuration + And without attribute "administration.network.server.https" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "La configuration du serveur n'indique pas la securisation du serveur" + + Scenario: [server.json] serveur http avec un mauvais host + Given a valid configuration + And with parameter "test" for attribute "administration.network.server.host" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "L'host du serveur est mal renseigne." + + Scenario: [server.json] serveur http avec un autre host + Given a valid configuration + And with parameter "127.0.0.1" for attribute "administration.network.server.host" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [server.json] serveur http sans host + Given a valid configuration + And without attribute "administration.network.server.host" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "La configuration du serveur n'indique aucun host" + + Scenario: [server.json] serveur http avec un mauvais port + Given a valid configuration + And with parameter "test" for attribute "administration.network.server.port" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Le port est mal renseigne." + + Scenario: [server.json] serveur http avec un port trop eleve + Given a valid configuration + And with parameter "65537" for attribute "administration.network.server.port" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Le port est mal renseigne: Numero de port invalide" + + Scenario: [server.json] serveur http avec un autre port + Given a valid configuration + And with parameter "8888" for attribute "administration.network.server.port" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [server.json] serveur http sans port + Given a valid configuration + And without attribute "administration.network.server.port" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "La configuration du serveur n'indique aucun port" + + Scenario: [server.json] serveur https bien configuré + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "/run/secrets/key" for attribute "administration.network.server.options.key" in server configuration + And with parameter "/run/secrets/cert" for attribute "administration.network.server.options.cert" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [server.json] serveur https avec un port different + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "/run/secrets/cert" for attribute "administration.network.server.options.cert" in server configuration + And with parameter "/run/secrets/key" for attribute "administration.network.server.options.key" in server configuration + And with parameter "445" for attribute "administration.network.server.port" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [server.json] serveur https sans options + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And without attribute "administration.network.server.options" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Serveur https sans options." + + Scenario: [server.json] serveur https avec options vide + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "" for attribute "administration.network.server.options" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Serveur https sans options." + + Scenario: [server.json] serveur https avec un mauvais options + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "test" for attribute "administration.network.server.options" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "L'objet options doit contenir un key pour le HTTPS." + + Scenario: [server.json] serveur https avec options.key vide + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "" for attribute "administration.network.server.options.key" in server configuration + And with parameter "/run/secrets/cert" for attribute "administration.network.server.options.cert" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "L'objet options doit contenir un key pour le HTTPS." + + Scenario: [server.json] serveur https avec un mauvais options.key + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "test.key" for attribute "administration.network.server.options.key" in server configuration + And with parameter "/run/secrets/cert" for attribute "administration.network.server.options.cert" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Le fichier ne peut etre lu" + + Scenario: [server.json] serveur https avec options.cert vide + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "" for attribute "administration.network.server.options.cert" in server configuration + And with parameter "/run/secrets/key" for attribute "administration.network.server.options.key" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "L'objet options doit contenir un cert pour le HTTPS" + + Scenario: [server.json] serveur https avec un mauvais options.cert + Given a valid configuration + And with parameter "true" for attribute "administration.network.server.https" in server configuration + And with parameter "/run/secrets/key" for attribute "administration.network.server.options.key" in server configuration + And with parameter "test.cert" for attribute "administration.network.server.options.cert" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Le fichier ne peut etre lu" + + + Scenario: [server.json] administration.api différent + Given a valid configuration + And with parameter "test" for attribute "administration.api" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration de l'api: 'name' absent" + + Scenario: [server.json] administration.api vide + Given a valid configuration + And with parameter "" for attribute "administration.api" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.api' absent" + + Scenario: [server.json] administration.api absent + Given a valid configuration + And without attribute "administration.api" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'administration.api' absent" + + Scenario: [server.json] administration.api.name différent + Given a valid configuration + And with parameter "test" for attribute "administration.api.name" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: 'name' ne peut être évalué" + + Scenario: [server.json] administration.api.name vide + Given a valid configuration + And with parameter "" for attribute "administration.api.name" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration de l'api: 'name' absent" + + Scenario: [server.json] administration.api.name absent + Given a valid configuration + And without attribute "administration.api.name" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration de l'api: 'name' absent" + + Scenario: [server.json] administration.api.version différent + Given a valid configuration + And with parameter "test" for attribute "administration.api.version" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration de l'api: 'version' ne peut être évalué" + + Scenario: [server.json] administration.api.version vide + Given a valid configuration + And with parameter "" for attribute "administration.api.version" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration de l'api: 'version' absent" + + Scenario: [server.json] administration.api.version absent + Given a valid configuration + And without attribute "administration.api.version" in server configuration + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration de l'api: 'version' absent" diff --git a/test/functional/configuration/cucumber/features/conf-common.feature b/test/functional/configuration/cucumber/features/conf-common.feature new file mode 100644 index 0000000..710f27d --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-common.feature @@ -0,0 +1,51 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: Configuration correcte + Given a valid configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + Then the server log should contain "La vérification des différentes configurations est terminée" + Then the server log should not contain "[ERROR]" + + Scenario: Mauvais argument de la ligne de commande (valeur vide) + Given a valid configuration + And with "" for command line parameter "ROAD2_CONF_FILE" + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Impossible de recuperer le chemin absolu du fichier de configuration" + + Scenario: Mauvais argument de la ligne de commande (fichier qui n'existe pas) + Given a valid configuration + And with "test1" for command line parameter "ROAD2_CONF_FILE" + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Mauvaise configuration: fichier de configuration global inexistant" + + #TODO Fix: root peut toujours lire un fichier même si chmod 000 + #Scenario: Mauvais argument de la ligne de commande (fichier qui ne peut être lu) + # Given a valid configuration + # And a server configuration non readable + # When I test the configuration + # Then the configuration analysis should give an exit code 1 + # Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration de Road2" + + Scenario: Pas d'argument ROAD2_CONF_FILE dans la ligne de commande + Given a valid configuration + And without parameter "ROAD2_CONF_FILE" in command line + When I test the configuration + Then the configuration analysis should give an exit code 11 + Then the server log should contain "Aucun fichier de configuration. Utiliser la variable d'environnement $ROAD2_CONF_FILE ou l'option --ROAD2_CONF_FILE lors de l'initialisation du serveur." + + Scenario: Argument inutile dans la ligne de commande + Given a valid configuration + And with "test" for command line parameter "ROAD2_TEST" + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + Then the server log should contain "La vérification des différentes configurations est terminée" diff --git a/test/functional/configuration/cucumber/features/conf-cors.feature b/test/functional/configuration/cucumber/features/conf-cors.feature new file mode 100644 index 0000000..fdfd023 --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-cors.feature @@ -0,0 +1,21 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: [cors.json] Contenu different + Given a valid configuration + And with parameter "127.0.0.1" for attribute "origin" in cors configuration + And with parameter "GET,POST" for attribute "methods" in cors configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + And the command log should not contain error + + Scenario: [cors.json] Contenu mauvais + Given a valid configuration + And with parameter "TEST" for attribute "methods" in cors configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + And the command log should not contain error diff --git a/test/functional/configuration/cucumber/features/conf-log.feature b/test/functional/configuration/cucumber/features/conf-log.feature new file mode 100644 index 0000000..9b9e40a --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-log.feature @@ -0,0 +1,88 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: [log.json] (mainConf different) + Given a valid configuration + And with parameter "test" for attribute "mainConf" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'mainConf.appenders' absent" + + Scenario: [log.json] (mainConf vide) + Given a valid configuration + And with parameter "" for attribute "mainConf" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'mainConf' absent" + + Scenario: [log.json] (mainConf absent) + Given a valid configuration + And without attribute "mainConf" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'mainConf' absent" + + Scenario: [log.json] (httpConf different) + Given a valid configuration + And with parameter "test" for attribute "httpConf" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.level' absent" + + Scenario: [log.json] (httpConf vide) + Given a valid configuration + And with parameter "" for attribute "httpConf" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf' absent" + + Scenario: [log.json] (httpConf absent) + Given a valid configuration + And without attribute "httpConf" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf' absent" + + Scenario: [log.json] (httpConf.level different) + Given a valid configuration + And with parameter "test" for attribute "httpConf.level" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [log.json] (httpConf.level vide) + Given a valid configuration + And with parameter "" for attribute "httpConf.level" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.level' absent" + + Scenario: [log.json] (httpConf.level absent) + Given a valid configuration + And without attribute "httpConf.level" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.level' absent" + + Scenario: [log.json] (httpConf.format different) + Given a valid configuration + And with parameter "test" for attribute "httpConf.format" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [log.json] (httpConf.format vide) + Given a valid configuration + And with parameter "" for attribute "httpConf.format" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.format' absent" + + Scenario: [log.json] (httpConf.format absent) + Given a valid configuration + And without attribute "httpConf.format" in service log configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.format' absent" diff --git a/test/functional/configuration/cucumber/features/conf-operation.feature b/test/functional/configuration/cucumber/features/conf-operation.feature new file mode 100644 index 0000000..0c194b0 --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-operation.feature @@ -0,0 +1,122 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: [operations] id différent + Given a valid configuration + And with parameter "test" for attribute "id" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation indiquee n'est pas disponible" + + Scenario: [operations] id vide + Given a valid configuration + And with parameter "" for attribute "id" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'id" + + Scenario: [operations] id absent + Given a valid configuration + And without attribute "id" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'id" + + Scenario: [operations] name différent + Given a valid configuration + And with parameter "test" for attribute "name" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [operations] name vide + Given a valid configuration + And with parameter "" for attribute "name" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'attribut name" + + Scenario: [operations] name absent + Given a valid configuration + And without attribute "name" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'attribut name" + + Scenario: [operations] description différent + Given a valid configuration + And with parameter "test" for attribute "description" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [operations] description vide + Given a valid configuration + And with parameter "" for attribute "description" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'attribut description" + + Scenario: [operations] description absent + Given a valid configuration + And without attribute "description" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'attribut description" + + Scenario: [operations] parameters différent + Given a valid configuration + And with parameter "test" for attribute "parameters" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Les parametres de l'operation ne sont pas dans un tableau" + + Scenario: [operations] parameters vide + Given a valid configuration + And with parameter "" for attribute "parameters" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'attribut parameters" + + Scenario: [operations] parameters absent + Given a valid configuration + And without attribute "parameters" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation ne contient pas d'attribut parameters" + + Scenario: [operations] parameters est un tableau vide + Given a valid configuration + And without attribute "parameters.[13]" in "route.json" operation + And without attribute "parameters.[12]" in "route.json" operation + And without attribute "parameters.[11]" in "route.json" operation + And without attribute "parameters.[10]" in "route.json" operation + And without attribute "parameters.[9]" in "route.json" operation + And without attribute "parameters.[8]" in "route.json" operation + And without attribute "parameters.[7]" in "route.json" operation + And without attribute "parameters.[6]" in "route.json" operation + And without attribute "parameters.[5]" in "route.json" operation + And without attribute "parameters.[4]" in "route.json" operation + And without attribute "parameters.[3]" in "route.json" operation + And without attribute "parameters.[2]" in "route.json" operation + And without attribute "parameters.[1]" in "route.json" operation + And without attribute "parameters.[0]" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le tableau des parametres est vide" + + Scenario: [operations] parameters est incomplet + Given a valid configuration + And without attribute "parameters.[0]" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le nombre de parametres presents n'est pas celui attendu" + + Scenario: [operations] parameters contient un element different + Given a valid configuration + And with parameter "test" for attribute "parameters.[0]" in "route.json" operation + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation précise un parametre qui n'est pas disponible" diff --git a/test/functional/configuration/cucumber/features/conf-osrm-ressource.feature b/test/functional/configuration/cucumber/features/conf-osrm-ressource.feature new file mode 100644 index 0000000..cae5bb7 --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-osrm-ressource.feature @@ -0,0 +1,21 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: [osrm resource] resource absent + Given a valid configuration + And without attribute "resource" in "data-osm.resource" resource + When I test the configuration + Then the configuration analysis should give an exit code 1 + # TODO : avoir plusieurs ressources dans le dossier pour qu'une d'entre elles marche + # Then the configuration analysis should give an exit code 0 + # Then the server log should contain "Erreur lors de la lecture de la ressource" + + Scenario: [osrm resource] id different + Given a valid configuration + And with parameter "test" for attribute "resource.id" in "data-osm.resource" resource + When I test the configuration + Then the configuration analysis should give an exit code 0 diff --git a/test/functional/configuration/cucumber/features/conf-parameter.feature b/test/functional/configuration/cucumber/features/conf-parameter.feature new file mode 100644 index 0000000..b5ebba4 --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-parameter.feature @@ -0,0 +1,307 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: [parameters] id différent + Given a valid configuration + And with parameter "test" for attribute "id" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'operation précise un parametre qui n'est pas disponible: intermediates" + + Scenario: [parameters] id vide + Given a valid configuration + And with parameter "" for attribute "id" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'id" + + Scenario: [parameters] id absent + Given a valid configuration + And without attribute "id" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'id" + + Scenario: [parameters] id deja utilise + Given a valid configuration + And with parameter "test" for attribute "id" in "start.json" parameter + And with parameter "test" for attribute "id" in "end.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre contenant l'id test est deja vérifié" + + Scenario: [parameters] name différent + Given a valid configuration + And with parameter "test" for attribute "name" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] name vide + Given a valid configuration + And with parameter "" for attribute "name" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut name" + + Scenario: [parameters] name absent + Given a valid configuration + And without attribute "name" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut name" + + Scenario: [parameters] description différent + Given a valid configuration + And with parameter "test" for attribute "description" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] description vide + Given a valid configuration + And with parameter "" for attribute "description" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut description" + + Scenario: [parameters] description absent + Given a valid configuration + And without attribute "description" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut description" + + Scenario: [parameters] required différent + Given a valid configuration + And with parameter "true" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] required invalide + Given a valid configuration + And with parameter "test" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre contient un attribut required mal configure" + + Scenario: [parameters] required vide + Given a valid configuration + And with parameter "" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut required" + + Scenario: [parameters] required absent + Given a valid configuration + And without attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut required" + + Scenario: [parameters] defaultValue différent (true donc probleme apres) + Given a valid configuration + And with parameter "true" for attribute "defaultValue" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas de valeur par defaut alors qu'il doit en avoir un" + + Scenario: [parameters] defaultValue invalide + Given a valid configuration + And with parameter "test" for attribute "defaultValue" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre contient un attribut defaultValue mal configure" + + Scenario: [parameters] defaultValue vide + Given a valid configuration + And with parameter "" for attribute "defaultValue" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut defaultValue" + + Scenario: [parameters] defaultValue absent + Given a valid configuration + And without attribute "defaultValue" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut defaultValue" + + Scenario: [parameters] example différent + Given a valid configuration + And with parameter "test" for attribute "example" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] example vide + Given a valid configuration + And with parameter "" for attribute "example" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le parametre ne contient pas d'exemple" + + Scenario: [parameters] example absent + Given a valid configuration + And without attribute "example" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le parametre ne contient pas d'exemple" + + Scenario: [parameters] type différent + Given a valid configuration + And with parameter "boolean" for attribute "type" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La valeur du parametre est incorrecte par rapport à son type: boolean" + + Scenario: [parameters] type invalide + Given a valid configuration + And with parameter "test" for attribute "type" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le type du parametre est incorrect" + + Scenario: [parameters] type vide + Given a valid configuration + And with parameter "" for attribute "type" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut type" + + Scenario: [parameters] type absent + Given a valid configuration + And without attribute "type" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre ne contient pas d'attribut type" + + Scenario: [parameters] min différent et required true + Given a valid configuration + And with parameter "1" for attribute "min" in "intermediates.json" parameter + And with parameter "true" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] min différent et required false + Given a valid configuration + And with parameter "1" for attribute "min" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre min est incorrect: valeur superieure a 0" + + Scenario: [parameters] min a 0 et required true + Given a valid configuration + And with parameter "0" for attribute "min" in "intermediates.json" parameter + And with parameter "true" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre min est incorrect: valeur inferieure a 1" + + Scenario: [parameters] min invalide + Given a valid configuration + And with parameter "test" for attribute "min" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre min est incorrect: valeur non entiere" + + Scenario: [parameters] min vide + Given a valid configuration + And with parameter "" for attribute "min" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] min absent + Given a valid configuration + And without attribute "min" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] max différent + Given a valid configuration + And with parameter "1" for attribute "max" in "intermediates.json" parameter + And with parameter "true" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] max a 0 + Given a valid configuration + And with parameter "0" for attribute "max" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre max est incorrect: valeur inferieure a 1" + + Scenario: [parameters] max invalide + Given a valid configuration + And with parameter "test" for attribute "max" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre max est incorrect: valeur non entiere" + + Scenario: [parameters] max vide + Given a valid configuration + And with parameter "" for attribute "max" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] max absent + Given a valid configuration + And without attribute "max" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] max plus petit que min + Given a valid configuration + And with parameter "1" for attribute "max" in "intermediates.json" parameter + And with parameter "2" for attribute "min" in "intermediates.json" parameter + And with parameter "true" for attribute "required" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre max est incorrect: valeur inferieure au parametre min" + + Scenario: [parameters] explode différent (true) + Given a valid configuration + And with parameter "true" for attribute "explode" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] explode invalide + Given a valid configuration + And with parameter "test" for attribute "explode" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre explode est incorrect" + + Scenario: [parameters] explode vide + Given a valid configuration + And with parameter "" for attribute "explode" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] explode absent + Given a valid configuration + And without attribute "explode" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [parameters] style invalide alors que explode false + Given a valid configuration + And with parameter "test" for attribute "style" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre style est incorrect" + + Scenario: [parameters] style vide alors que explode false + Given a valid configuration + And with parameter "" for attribute "style" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre style n'est pas present alors que explode=false" + + Scenario: [parameters] style vide alors que explode false + Given a valid configuration + And without attribute "style" in "intermediates.json" parameter + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre style n'est pas present alors que explode=false" diff --git a/test/functional/configuration/cucumber/features/conf-projection.feature b/test/functional/configuration/cucumber/features/conf-projection.feature new file mode 100644 index 0000000..89f4376 --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-projection.feature @@ -0,0 +1,110 @@ +# Tests fonctionnels de Road2 sur la configuration du serveur +Feature: Road2 configuration + Tests fonctionnels de Road2 sur la configuration du serveur + + Background: + Given I have loaded all my test configuration + + Scenario: [projection.json] JSON invalide + Given a valid configuration + And a wrong JSON file "./projections/test.json" + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de projection" + + Scenario: [projection.json] JSON vide + Given a valid configuration + And without attribute "projectionsList" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La fichier des projections ne contient pas de liste" + + Scenario: [projection.json] projectionsList absent + Given a valid configuration + And without attribute "projectionsList" in "projection.json" projection + And with parameter "test" for attribute "projections" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La fichier des projections ne contient pas de liste" + + Scenario: [projection.json] projectionsList different + Given a valid configuration + And with parameter "test" for attribute "projectionsList" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'attribut projectionsList de la configuration n'est pas un tableau" + + Scenario: [projection.json] projectionsList est un tableau vide + Given a valid configuration + And without attribute "projectionsList.[6]" in "projection.json" projection + And without attribute "projectionsList.[5]" in "projection.json" projection + And without attribute "projectionsList.[4]" in "projection.json" projection + And without attribute "projectionsList.[3]" in "projection.json" projection + And without attribute "projectionsList.[2]" in "projection.json" projection + And without attribute "projectionsList.[1]" in "projection.json" projection + And without attribute "projectionsList.[0]" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'attribut projectionsList de la configuration est un tableau vide" + + Scenario: [projection.json] une configuration de projection vide + Given a valid configuration + And without attribute "projectionsList.[0].id" in "projection.json" projection + And without attribute "projectionsList.[0].parameters" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration de la projection n'a pas d'id" + + Scenario: [projection.json] une configuration de projection sans id + Given a valid configuration + And without attribute "projectionsList.[0].id" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration de la projection n'a pas d'id" + + Scenario: [projection.json] une configuration de projection sans parametre + Given a valid configuration + And without attribute "projectionsList.[0].parameters" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration de la projection n'a pas de parametres" + + Scenario: [projection.json] une configuration de projection différentes + Given a valid configuration + And with parameter "test" for attribute "projectionsList.[0]" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration de la projection n'a pas d'id" + + Scenario: [projection.json] projection.id différent pour une projection inutilisée dans les topologies + Given a valid configuration + And with parameter "test" for attribute "projectionsList.[2].id" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [projection.json] projection.id différent pour une projection utilisée dans les topologies + Given a valid configuration + And with parameter "test" for attribute "projectionsList.[0].id" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + + Scenario: [projection.json] projection.id vide + Given a valid configuration + And with parameter "" for attribute "projectionsList.[2].id" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration de la projection n'a pas d'id" + + #TODO : Voir si on peut mieux vérifier + Scenario: [projection.json] projection.parameters différent + Given a valid configuration + And with parameter "test" for attribute "projectionsList.[0].parameters" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [projection.json] projection.parameters vide + Given a valid configuration + And with parameter "" for attribute "projectionsList.[0].parameters" in "projection.json" projection + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration de la projection n'a pas de parametres" diff --git a/test/functional/configuration/cucumber/features/conf-service.feature b/test/functional/configuration/cucumber/features/conf-service.feature new file mode 100644 index 0000000..91f70fe --- /dev/null +++ b/test/functional/configuration/cucumber/features/conf-service.feature @@ -0,0 +1,677 @@ +# Tests fonctionnels de Road2 sur la configuration du service +Feature: Road2 service configuration + Tests fonctionnels de Road2 sur la configuration du service + + Background: + Given I have loaded all my test configuration + + Scenario: [service.json] (application different) + Given a valid configuration + And with parameter "test" for attribute "application" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:name' manquant" + + Scenario: [service.json] (application vide) + Given a valid configuration + And with parameter "" for attribute "application" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application' manquant" + + Scenario: [service.json] (application absent) + Given a valid configuration + And without attribute "application" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application' manquant" + + Scenario: [service.json] (application.logs different) + Given a valid configuration + And with parameter "test" for attribute "application.logs" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application.logs.configuration' manquant" + + Scenario: [service.json] (application.logs vide) + Given a valid configuration + And with parameter "" for attribute "application.logs" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application.logs' manquant" + + Scenario: [service.json] (application.logs absent) + Given a valid configuration + And without attribute "application.logs" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application.logs' manquant" + + Scenario: [service.json] (application.logs.configuration different) + Given a valid configuration + And with parameter "test" for attribute "application.logs.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Fichier de conf des logs inexistant" + + Scenario: [service.json] (application.logs.configuration vide) + Given a valid configuration + And with parameter "" for attribute "application.logs.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application.logs.configuration' manquant" + + Scenario: [service.json] (application.logs.configuration absent) + Given a valid configuration + And without attribute "application.logs.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application.logs.configuration' manquant" + + Scenario: [service.json] (application.logs.configuration sur un fichier non lisible) + Given a valid configuration + And a file "file.json" non readable + And with parameter "file.json" for attribute "application.logs.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de conf des logs du service" + + Scenario: [service.json] (application.logs.configuration sur un fichier vide) + Given a valid configuration + And a file "file.json" + And with parameter "file.json" for attribute "application.logs.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de conf des logs du service" + + Scenario: [service.json] (application.logs.configuration sur un fichier incorrect) + Given a valid configuration + And a wrong JSON file "file.json" + And with parameter "file.json" for attribute "application.logs.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de conf des logs du service" + + Scenario: [service.json] (name different) + Given a valid configuration + And with parameter "test" for attribute "application.name" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [service.json] (name vide) + Given a valid configuration + And with parameter "" for attribute "application.name" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + + Scenario: [service.json] (name absent) + Given a valid configuration + And without attribute "application.name" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + + Scenario: [service.json] (title different) + Given a valid configuration + And with parameter "test" for attribute "application.title" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [service.json] (title vide) + Given a valid configuration + And with parameter "" for attribute "application.title" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:title' manquant !" + + Scenario: [service.json] (title absent) + Given a valid configuration + And without attribute "application.title" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:title' manquant !" + + Scenario: [service.json] (description different) + Given a valid configuration + And with parameter "test" for attribute "application.description" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [service.json] (description vide) + Given a valid configuration + And with parameter "" for attribute "application.description" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:description' manquant !" + + Scenario: [service.json] (description absent) + Given a valid configuration + And without attribute "application.description" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:description' manquant !" + + Scenario: [service.json] (provider different) + Given a valid configuration + And with parameter "test" for attribute "application.provider" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:provider:name' manquant !" + + Scenario: [service.json] (provider vide) + Given a valid configuration + And with parameter "" for attribute "application.provider" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + Then the server log should contain "Configuration incomplete: Objet 'application:provider' manquant !" + + Scenario: [service.json] (provider absent) + Given a valid configuration + And without attribute "application.provider" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + Then the server log should contain "Configuration incomplete: Objet 'application:provider' manquant !" + + Scenario: [service.json] (provider.name different) + Given a valid configuration + And with parameter "test" for attribute "application.provider.name" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [service.json] (provider.name vide) + Given a valid configuration + And with parameter "" for attribute "application.provider.name" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:provider:name' manquant !" + + Scenario: [service.json] (provider.name absent) + Given a valid configuration + And without attribute "application.provider.name" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:provider:name' manquant !" + + Scenario: [service.json] (provider.site different) + Given a valid configuration + And with parameter "test" for attribute "application.provider.site" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [service.json] (provider.site vide) + Given a valid configuration + And with parameter "" for attribute "application.provider.site" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le champ 'application:provider:site' n'est pas renseigne." + + Scenario: [service.json] (provider.site absent) + Given a valid configuration + And without attribute "application.provider.site" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le champ 'application:provider:site' n'est pas renseigne." + + Scenario: [service.json] (provider.mail different) + Given a valid configuration + And with parameter "test" for attribute "application.provider.mail" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [service.json] (provider.mail vide) + Given a valid configuration + And with parameter "" for attribute "application.provider.mail" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:provider:mail' manquant !" + + Scenario: [service.json] (provider.mail absent) + Given a valid configuration + And without attribute "application.provider.mail" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:provider:mail' manquant !" + + Scenario: [service.json] (operations different) + Given a valid configuration + And with parameter "test" for attribute "application.operations" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:operations:directory' manquant !" + + Scenario: [service.json] (operations vide) + Given a valid configuration + And with parameter "" for attribute "application.operations" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:operations' manquant !" + + Scenario: [service.json] (operations absent) + Given a valid configuration + And without attribute "application.operations" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:operations' manquant !" + + Scenario: [service.json] (operations.directory sur un dossier qui n'existe pas) + Given a valid configuration + And with parameter "test" for attribute "application.operations.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le dossier des operations est mal configuré" + + Scenario: [service.json] (operations.directory vide) + Given a valid configuration + And with parameter "" for attribute "application.operations.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:operations:directory' manquant !" + + Scenario: [service.json] (operations.directory absent) + Given a valid configuration + And without attribute "application.operations.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:operations:directory' manquant !" + + # TODO + # Faire un scénario qui teste si les fichiers de operations.directory ne sont pas lisibles + + Scenario: [service.json] (operations.parameters different) + Given a valid configuration + And with parameter "test" for attribute "application.operations.parameters" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !" + + Scenario: [service.json] (operations.parameters vide) + Given a valid configuration + And with parameter "" for attribute "application.operations.parameters" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:operations:parameters' manquant !" + + Scenario: [service.json] (operations.parameters absent) + Given a valid configuration + And without attribute "application.operations.parameters" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:operations:parameters' manquant !" + + Scenario: [service.json] (operations.parameters.directory sur un dossier qui n'existe pas) + Given a valid configuration + And with parameter "test" for attribute "application.operations.parameters.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Le dossier des parametres n'existe pas" + + Scenario: [service.json] (operations.parameters.directory vide) + Given a valid configuration + And with parameter "" for attribute "application.operations.parameters.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !" + + Scenario: [service.json] (operations.parameters.directory absent) + Given a valid configuration + And without attribute "application.operations.parameters.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !" + + # TODO + # Faire un scénario qui teste si les fichiers de operations.parameters.directory ne sont pas lisibles + + Scenario: [service.json] (resources different) + Given a valid configuration + And with parameter "test" for attribute "application.resources" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' manquant !" + + Scenario: [service.json] (resources vide) + Given a valid configuration + And with parameter "" for attribute "application.resources" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:resources' manquant !" + + Scenario: [service.json] (resources absent) + Given a valid configuration + And without attribute "application.resources" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:resources' manquant !" + + Scenario: [service.json] (resources.directories est une chaine de caracteres) + Given a valid configuration + And with parameter "test" for attribute "application.resources.directories" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' n'est pas un tableau !" + + # TODO + # Tester le paramètre resources.directories sur un tableau vide + + Scenario: [service.json] (resources.directories sur un dossier qui n'existe pas et en chemin relatif) + Given a valid configuration + And with parameter "test" for attribute "application.resources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas:" + + Scenario: [service.json] (resources.directories sur deux dossiers qui n'existent pas et en chemins relatifs) + Given a valid configuration + And with parameter "test" for attribute "application.resources.directories.[0]" in service configuration + And with parameter "test1" for attribute "application.resources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas" + + Scenario: [service.json] (resources.directories contient un élément vide) + Given a valid configuration + And with parameter "" for attribute "application.resources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' contient un élément vide" + + Scenario: [service.json] (resources.directories contient que des éléments vides) + Given a valid configuration + And with parameter "" for attribute "application.resources.directories.[0]" in service configuration + And with parameter "" for attribute "application.resources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Aucun dossier de ressource n'a été validé" + + Scenario: [service.json] (resources.directories absent) + Given a valid configuration + And without attribute "application.resources.directories" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' manquant !" + + # TODO + # Tester un dossier de ressources dont une des ressources ne peut être lues + + Scenario: [service.json] (network different) + Given a valid configuration + And with parameter "test" for attribute "application.network" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' manquant !" + + Scenario: [service.json] (network vide) + Given a valid configuration + And with parameter "" for attribute "application.network" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:network' manquant !" + + Scenario: [service.json] (network absent) + Given a valid configuration + And without attribute "application.network" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:network' manquant !" + + Scenario: [service.json] (network.servers different) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' n'est pas un tableau !" + + Scenario: [service.json] (network.servers vide) + Given a valid configuration + And with parameter "" for attribute "application.network.servers" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' manquant !" + + Scenario: [service.json] (network.servers absent) + Given a valid configuration + And without attribute "application.network.servers" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' manquant !" + + # TODO: tester le tableau vide pour network.servers + + Scenario: [service.json] (network.servers contient une chaîne de caractères) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[0]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration d'un serveur !" + + Scenario: [service.json] (network.servers contient que des chaînes de caractères) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[0]" in service configuration + And with parameter "test" for attribute "application.network.servers.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration d'un serveur !" + + Scenario: [service.json] (serveur http avec id different) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[0].id" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the command log should not contain error + + Scenario: [service.json] (serveur http sans id) + Given a valid configuration + And without attribute "application.network.servers.[0].id" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration du serveur n'indique aucun id" + + Scenario: [service.json] (serveur http avec un mauvais https) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[0].https" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le parametre https est mal renseigné. Valeurs disponibles: 'true' ou 'false'." + + Scenario: [service.json] (serveur http sans https) + Given a valid configuration + And without attribute "application.network.servers.[0].https" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration du serveur n'indique pas la securisation du serveur" + + Scenario: [service.json] (serveur http avec un mauvais host) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[0].host" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'host du serveur est mal renseigne." + + Scenario: [service.json] (serveur http avec un autre host) + Given a valid configuration + And with parameter "127.0.0.1" for attribute "application.network.servers.[0].host" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [service.json] (serveur http sans host) + Given a valid configuration + And without attribute "application.network.servers.[0].host" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration du serveur n'indique aucun host" + + Scenario: [service.json] (serveur http avec un mauvais port) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[0].port" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le port est mal renseigne." + + Scenario: [service.json] (serveur http avec un port trop eleve) + Given a valid configuration + And with parameter "65537" for attribute "application.network.servers.[0].port" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le port est mal renseigne: Numero de port invalide" + + Scenario: [service.json] (serveur http avec un autre port) + Given a valid configuration + And with parameter "8888" for attribute "application.network.servers.[0].port" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [service.json] (serveur http sans port) + Given a valid configuration + And without attribute "application.network.servers.[0].port" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "La configuration du serveur n'indique aucun port" + + Scenario: [service.json] (serveur https avec un port different) + Given a valid configuration + And with parameter "445" for attribute "application.network.servers.[1].port" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + + Scenario: [service.json] (serveur https sans options) + Given a valid configuration + And without attribute "application.network.servers.[1].options" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Serveur https sans options." + + Scenario: [service.json] (server https avec options vide) + Given a valid configuration + And with parameter "" for attribute "application.network.servers.[1].options" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Serveur https sans options." + + Scenario: [service.json] (serveur https avec un mauvais options) + Given a valid configuration + And with parameter "test" for attribute "application.network.servers.[1].options" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'objet options doit contenir un key pour le HTTPS." + + Scenario: [service.json] (server https avec options.key vide) + Given a valid configuration + And with parameter "" for attribute "application.network.servers.[1].options.key" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'objet options doit contenir un key pour le HTTPS." + + Scenario: [service.json] (serveur https avec un mauvais options.key) + Given a valid configuration + And with parameter "test.key" for attribute "application.network.servers.[1].options.key" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le fichier ne peut etre lu" + + Scenario: [service.json] (server https avec options.cert vide) + Given a valid configuration + And with parameter "" for attribute "application.network.servers.[1].options.cert" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "L'objet options doit contenir un cert pour le HTTPS." + + Scenario: [service.json] (serveur https avec un mauvais options.cert) + Given a valid configuration + And with parameter "test.cert" for attribute "application.network.servers.[1].options.cert" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Le fichier ne peut etre lu" + + Scenario: [service.json] (network.cors absent) + Given a valid configuration + And without attribute "application.network.cors" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Configuration incomplete: Objet 'application:network:cors' manquant !" + + Scenario: [service.json] (network.cors vide) + Given a valid configuration + And with parameter "" for attribute "application.network.cors" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Configuration incomplete: Objet 'application:network:cors' manquant !" + + Scenario: [service.json] (network.cors mauvais) + Given a valid configuration + And with parameter "test" for attribute "application.network.cors" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:network:cors:configuration' manquant !" + + Scenario: [service.json] (network.cors.configuration vide) + Given a valid configuration + And with parameter "" for attribute "application.network.cors.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:network:cors:configuration' manquant !" + + Scenario: [service.json] (network.cors.configuration mauvais) + Given a valid configuration + And with parameter "test.json" for attribute "application.network.cors.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Fichier de cors inexistant" + + Scenario: [service.json] (application.network.cors.configuration sur un fichier incorrect) + Given a valid configuration + And a wrong JSON file "file.json" + And with parameter "file.json" for attribute "application.network.cors.configuration" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de cors de Road2" + + Scenario: [service.json] (projections different) + Given a valid configuration + And with parameter "test" for attribute "application.projections" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:projections:directory' manquant !" + + Scenario: [service.json] (projections vide) + Given a valid configuration + And with parameter "" for attribute "application.projections" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:projections' manquant !" + + Scenario: [service.json] (projections absent) + Given a valid configuration + And without attribute "application.projections" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:projections' manquant !" + + Scenario: [service.json] (projections.directory sur un dossier qui n'existe pas) + Given a valid configuration + And with parameter "test" for attribute "application.projections.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Dossier de projections inexistant" + + Scenario: [service.json] (projections.directory vide) + Given a valid configuration + And with parameter "" for attribute "application.projections.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:projections:directory' manquant !" + + Scenario: [service.json] (projections.directory absent) + Given a valid configuration + And without attribute "application.projections.directory" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:projections:directory' manquant !" + + # TODO + # Faire un scénario qui teste si les fichiers de projections.directory ne sont pas lisibles diff --git a/test/functional/configuration/cucumber/features/configurationTest.feature b/test/functional/configuration/cucumber/features/configurationTest.feature deleted file mode 100644 index 1daf995..0000000 --- a/test/functional/configuration/cucumber/features/configurationTest.feature +++ /dev/null @@ -1,1354 +0,0 @@ -# Tests fonctionnels de Road2 sur la configuration du serveur -Feature: Road2 configuration - Tests fonctionnels de Road2 sur la configuration du serveur - - Background: - Given I have loaded all my test configuration - - Scenario: Configuration correcte - Given a valid configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "La vérification de la configuration est terminée" - Then the server log should not contain error - - Scenario: Mauvais argument de la ligne de commande (valeur vide) - Given a valid configuration - And with "" for command line parameter "ROAD2_CONF_FILE" - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Impossible de recuperer le chemin absolu du fichier de configuration" - - Scenario: Mauvais argument de la ligne de commande (fichier qui n'existe pas) - Given a valid configuration - And with "test1" for command line parameter "ROAD2_CONF_FILE" - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: fichier de configuration global inexistant" - - #TODO Fix: root peut toujours lire un fichier même si chmod 000 - #Scenario: Mauvais argument de la ligne de commande (fichier qui ne peut être lu) - # Given a valid configuration - # And a server configuration non readable - # When I test the configuration - # Then the configuration analysis should give an exit code 1 - # Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration de Road2" - - Scenario: Pas d'argument ROAD2_CONF_FILE dans la ligne de commande - Given a valid configuration - And without parameter "ROAD2_CONF_FILE" in command line - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Aucun fichier de configuration. Utiliser la variable d'environnement $ROAD2_CONF_FILE ou l'option --ROAD2_CONF_FILE lors de l'initialisation du serveur." - - Scenario: Argument inutile dans la ligne de commande - Given a valid configuration - And with "test" for command line parameter "ROAD2_TEST" - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "La vérification de la configuration est terminée" - Then the server log should not contain error - - Scenario: [server.json] (application different) - Given a valid configuration - And with parameter "test" for attribute "application" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application.logs' absent" - - Scenario: [server.json] (application vide) - Given a valid configuration - And with parameter "" for attribute "application" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application' absent" - - Scenario: [server.json] (application absent) - Given a valid configuration - And without attribute "application" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application' absent" - - Scenario: [server.json] (application.logs different) - Given a valid configuration - And with parameter "test" for attribute "application.logs" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application.logs.configuration' absent" - - Scenario: [server.json] (application.logs vide) - Given a valid configuration - And with parameter "" for attribute "application.logs" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application.logs' absent" - - Scenario: [server.json] (application.logs absent) - Given a valid configuration - And without attribute "application.logs" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application.logs' absent" - - Scenario: [server.json] (application.logs.configuration different) - Given a valid configuration - And with parameter "test" for attribute "application.logs.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: fichier de configuration des logs inexistant" - - Scenario: [server.json] (application.logs.configuration vide) - Given a valid configuration - And with parameter "" for attribute "application.logs.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application.logs.configuration' absent" - - Scenario: [server.json] (application.logs.configuration absent) - Given a valid configuration - And without attribute "application.logs.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvais configuration: 'application.logs.configuration' absent" - - Scenario: [server.json] (application.logs.configuration sur un fichier non lisible) - Given a valid configuration - And a file "file.json" non readable - And with parameter "file.json" for attribute "application.logs.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs" - - Scenario: [server.json] (application.logs.configuration sur un fichier vide) - Given a valid configuration - And a file "file.json" - And with parameter "file.json" for attribute "application.logs.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs" - - Scenario: [server.json] (application.logs.configuration sur un fichier incorrect) - Given a valid configuration - And a wrong JSON file "file.json" - And with parameter "file.json" for attribute "application.logs.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de configuration des logs" - - Scenario: [server.json] (name different) - Given a valid configuration - And with parameter "test" for attribute "application.name" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [server.json] (name vide) - Given a valid configuration - And with parameter "" for attribute "application.name" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - - Scenario: [server.json] (name absent) - Given a valid configuration - And without attribute "application.name" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - - Scenario: [server.json] (title different) - Given a valid configuration - And with parameter "test" for attribute "application.title" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - - Scenario: [server.json] (title vide) - Given a valid configuration - And with parameter "" for attribute "application.title" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:title' manquant !" - - Scenario: [server.json] (title absent) - Given a valid configuration - And without attribute "application.title" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:title' manquant !" - - Scenario: [server.json] (description different) - Given a valid configuration - And with parameter "test" for attribute "application.description" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - - Scenario: [server.json] (description vide) - Given a valid configuration - And with parameter "" for attribute "application.description" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:description' manquant !" - - Scenario: [server.json] (description absent) - Given a valid configuration - And without attribute "application.description" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:description' manquant !" - - Scenario: [server.json] (provider different) - Given a valid configuration - And with parameter "test" for attribute "application.provider" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:provider:name' manquant !" - - Scenario: [server.json] (provider vide) - Given a valid configuration - And with parameter "" for attribute "application.provider" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - Then the server log should contain "Configuration incomplete: Objet 'application:provider' manquant !" - - Scenario: [server.json] (provider absent) - Given a valid configuration - And without attribute "application.provider" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - Then the server log should contain "Configuration incomplete: Objet 'application:provider' manquant !" - - Scenario: [server.json] (provider.name different) - Given a valid configuration - And with parameter "test" for attribute "application.provider.name" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - - Scenario: [server.json] (provider.name vide) - Given a valid configuration - And with parameter "" for attribute "application.provider.name" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:provider:name' manquant !" - - Scenario: [server.json] (provider.name absent) - Given a valid configuration - And without attribute "application.provider.name" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:provider:name' manquant !" - - Scenario: [server.json] (provider.site different) - Given a valid configuration - And with parameter "test" for attribute "application.provider.site" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - - Scenario: [server.json] (provider.site vide) - Given a valid configuration - And with parameter "" for attribute "application.provider.site" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Le champ 'application:provider:site' n'est pas renseigne." - - Scenario: [server.json] (provider.site absent) - Given a valid configuration - And without attribute "application.provider.site" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Le champ 'application:provider:site' n'est pas renseigne." - - Scenario: [server.json] (provider.mail different) - Given a valid configuration - And with parameter "test" for attribute "application.provider.mail" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - - Scenario: [server.json] (provider.mail vide) - Given a valid configuration - And with parameter "" for attribute "application.provider.mail" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:provider:mail' manquant !" - - Scenario: [server.json] (provider.mail absent) - Given a valid configuration - And without attribute "application.provider.mail" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:provider:mail' manquant !" - - Scenario: [server.json] (operations different) - Given a valid configuration - And with parameter "test" for attribute "application.operations" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:operations:directory' manquant !" - - Scenario: [server.json] (operations vide) - Given a valid configuration - And with parameter "" for attribute "application.operations" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:operations' manquant !" - - Scenario: [server.json] (operations absent) - Given a valid configuration - And without attribute "application.operations" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:operations' manquant !" - - Scenario: [server.json] (operations.directory sur un dossier qui n'existe pas) - Given a valid configuration - And with parameter "test" for attribute "application.operations.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Le dossier des operations n'existe pas" - - Scenario: [server.json] (operations.directory vide) - Given a valid configuration - And with parameter "" for attribute "application.operations.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:operations:directory' manquant !" - - Scenario: [server.json] (operations.directory absent) - Given a valid configuration - And without attribute "application.operations.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:operations:directory' manquant !" - - # TODO - # Faire un scénario qui teste si les fichiers de operations.directory ne sont pas lisibles - - Scenario: [server.json] (operations.parameters different) - Given a valid configuration - And with parameter "test" for attribute "application.operations.parameters" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !" - - Scenario: [server.json] (operations.parameters vide) - Given a valid configuration - And with parameter "" for attribute "application.operations.parameters" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:operations:parameters' manquant !" - - Scenario: [server.json] (operations.parameters absent) - Given a valid configuration - And without attribute "application.operations.parameters" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:operations:parameters' manquant !" - - Scenario: [server.json] (operations.parameters.directory sur un dossier qui n'existe pas) - Given a valid configuration - And with parameter "test" for attribute "application.operations.parameters.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Le dossier des parametres n'existe pas" - - Scenario: [server.json] (operations.parameters.directory vide) - Given a valid configuration - And with parameter "" for attribute "application.operations.parameters.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !" - - Scenario: [server.json] (operations.parameters.directory absent) - Given a valid configuration - And without attribute "application.operations.parameters.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:operations:parameters:directory' manquant !" - - # TODO - # Faire un scénario qui teste si les fichiers de operations.parameters.directory ne sont pas lisibles - - Scenario: [server.json] (resources different) - Given a valid configuration - And with parameter "test" for attribute "application.resources" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' manquant !" - - Scenario: [server.json] (resources vide) - Given a valid configuration - And with parameter "" for attribute "application.resources" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:resources' manquant !" - - Scenario: [server.json] (resources absent) - Given a valid configuration - And without attribute "application.resources" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:resources' manquant !" - - Scenario: [server.json] (resources.directories est une chaine de caracteres) - Given a valid configuration - And with parameter "test" for attribute "application.resources.directories" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' n'est pas un tableau !" - - # TODO - # Tester le paramètre resources.directories sur un tableau vide - - Scenario: [server.json] (resources.directories sur un dossier qui n'existe pas et en chemin relatif) - Given a valid configuration - And with parameter "test" for attribute "application.resources.directories.[1]" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas:" - - Scenario: [server.json] (resources.directories sur deux dossiers qui n'existent pas et en chemins relatifs) - Given a valid configuration - And with parameter "test" for attribute "application.resources.directories.[0]" in server configuration - And with parameter "test1" for attribute "application.resources.directories.[1]" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' ne pointe vers aucune ressource disponible !" - - Scenario: [server.json] (resources.directories contient un élément vide) - Given a valid configuration - And with parameter "" for attribute "application.resources.directories.[1]" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' contient un élément vide" - - Scenario: [server.json] (resources.directories contient que des éléments vides) - Given a valid configuration - And with parameter "" for attribute "application.resources.directories.[0]" in server configuration - And with parameter "" for attribute "application.resources.directories.[1]" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' ne pointe vers aucune ressource disponible !" - - Scenario: [server.json] (resources.directories absent) - Given a valid configuration - And without attribute "application.resources.directories" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' manquant !" - - # TODO - # Tester un dossier de ressources dont une des ressources ne peut être lues - - Scenario: [server.json] (network different) - Given a valid configuration - And with parameter "test" for attribute "application.network" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' manquant !" - - Scenario: [server.json] (network vide) - Given a valid configuration - And with parameter "" for attribute "application.network" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:network' manquant !" - - Scenario: [server.json] (network absent) - Given a valid configuration - And without attribute "application.network" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:network' manquant !" - - Scenario: [server.json] (network.servers different) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' n'est pas un tableau !" - - Scenario: [server.json] (network.servers vide) - Given a valid configuration - And with parameter "" for attribute "application.network.servers" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' manquant !" - - Scenario: [server.json] (network.servers absent) - Given a valid configuration - And without attribute "application.network.servers" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:network:servers' manquant !" - - # TODO: tester le tableau vide pour network.servers - - Scenario: [server.json] (network.servers contient une chaîne de caractères) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[0]" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration d'un serveur !" - - Scenario: [server.json] (network.servers contient que des chaînes de caractères) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[0]" in server configuration - And with parameter "test" for attribute "application.network.servers.[1]" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration d'un serveur !" - - Scenario: [server.json] (serveur http avec id different) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[0].id" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should not contain error - - Scenario: [server.json] (serveur http sans id) - Given a valid configuration - And without attribute "application.network.servers.[0].id" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration du serveur n'indique aucun id" - - Scenario: [server.json] (serveur http avec un mauvais https) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[0].https" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre https est mal renseigné. Valeurs disponibles: 'true' ou 'false'." - - Scenario: [server.json] (serveur http sans https) - Given a valid configuration - And without attribute "application.network.servers.[0].https" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration du serveur n'indique pas la securisation du serveur" - - Scenario: [server.json] (serveur http avec un mauvais host) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[0].host" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'host du serveur est mal renseigne." - - Scenario: [server.json] (serveur http avec un autre host) - Given a valid configuration - And with parameter "127.0.0.1" for attribute "application.network.servers.[0].host" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [server.json] (serveur http sans host) - Given a valid configuration - And without attribute "application.network.servers.[0].host" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration du serveur n'indique aucun host" - - Scenario: [server.json] (serveur http avec un mauvais port) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[0].port" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le port est mal renseigne." - - Scenario: [server.json] (serveur http avec un port trop eleve) - Given a valid configuration - And with parameter "65537" for attribute "application.network.servers.[0].port" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le port est mal renseigne: Numero de port invalide" - - Scenario: [server.json] (serveur http avec un autre port) - Given a valid configuration - And with parameter "8888" for attribute "application.network.servers.[0].port" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [server.json] (serveur http sans port) - Given a valid configuration - And without attribute "application.network.servers.[0].port" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration du serveur n'indique aucun port" - - Scenario: [server.json] (serveur https avec un port different) - Given a valid configuration - And with parameter "445" for attribute "application.network.servers.[1].port" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [server.json] (serveur https sans options) - Given a valid configuration - And without attribute "application.network.servers.[1].options" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Serveur https sans options." - - Scenario: [server.json] (server https avec options vide) - Given a valid configuration - And with parameter "" for attribute "application.network.servers.[1].options" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Serveur https sans options." - - Scenario: [server.json] (serveur https avec un mauvais options) - Given a valid configuration - And with parameter "test" for attribute "application.network.servers.[1].options" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'objet options doit contenir un key pour le HTTPS." - - Scenario: [server.json] (server https avec options.key vide) - Given a valid configuration - And with parameter "" for attribute "application.network.servers.[1].options.key" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'objet options doit contenir un key pour le HTTPS." - - Scenario: [server.json] (serveur https avec un mauvais options.key) - Given a valid configuration - And with parameter "test.key" for attribute "application.network.servers.[1].options.key" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le fichier ne peut etre lu" - - Scenario: [server.json] (server https avec options.cert vide) - Given a valid configuration - And with parameter "" for attribute "application.network.servers.[1].options.cert" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'objet options doit contenir un cert pour le HTTPS." - - Scenario: [server.json] (serveur https avec un mauvais options.cert) - Given a valid configuration - And with parameter "test.cert" for attribute "application.network.servers.[1].options.cert" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le fichier ne peut etre lu" - - Scenario: [server.json] (network.cors absent) - Given a valid configuration - And without attribute "application.network.cors" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Configuration incomplete: Objet 'application:network:cors' manquant !" - - Scenario: [server.json] (network.cors vide) - Given a valid configuration - And with parameter "" for attribute "application.network.cors" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Configuration incomplete: Objet 'application:network:cors' manquant !" - - Scenario: [server.json] (network.cors mauvais) - Given a valid configuration - And with parameter "test" for attribute "application.network.cors" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:network:cors:configuration' manquant !" - - Scenario: [server.json] (network.cors.configuration vide) - Given a valid configuration - And with parameter "" for attribute "application.network.cors.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:network:cors:configuration' manquant !" - - Scenario: [server.json] (network.cors.configuration mauvais) - Given a valid configuration - And with parameter "test.json" for attribute "application.network.cors.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Fichier de cors inexistant" - - Scenario: [server.json] (application.network.cors.configuration sur un fichier incorrect) - Given a valid configuration - And a wrong JSON file "file.json" - And with parameter "file.json" for attribute "application.network.cors.configuration" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de cors de Road2" - - Scenario: [server.json] (projections different) - Given a valid configuration - And with parameter "test" for attribute "application.projections" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:projections:directory' manquant !" - - Scenario: [server.json] (projections vide) - Given a valid configuration - And with parameter "" for attribute "application.projections" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:projections' manquant !" - - Scenario: [server.json] (projections absent) - Given a valid configuration - And without attribute "application.projections" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Objet 'application:projections' manquant !" - - Scenario: [server.json] (projections.directory sur un dossier qui n'existe pas) - Given a valid configuration - And with parameter "test" for attribute "application.projections.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Dossier de projections inexistant" - - Scenario: [server.json] (projections.directory vide) - Given a valid configuration - And with parameter "" for attribute "application.projections.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:projections:directory' manquant !" - - Scenario: [server.json] (projections.directory absent) - Given a valid configuration - And without attribute "application.projections.directory" in server configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: Champ 'application:projections:directory' manquant !" - - # TODO - # Faire un scénario qui teste si les fichiers de projections.directory ne sont pas lisibles - - Scenario: [log.json] (mainConf different) - Given a valid configuration - And with parameter "test" for attribute "mainConf" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration des logs dans mainConf" - - Scenario: [log.json] (mainConf vide) - Given a valid configuration - And with parameter "" for attribute "mainConf" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'mainConf' absent" - - Scenario: [log.json] (mainConf absent) - Given a valid configuration - And without attribute "mainConf" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'mainConf' absent" - - Scenario: [log.json] (httpConf different) - Given a valid configuration - And with parameter "test" for attribute "httpConf" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.level' absent" - - Scenario: [log.json] (httpConf vide) - Given a valid configuration - And with parameter "" for attribute "httpConf" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf' absent" - - Scenario: [log.json] (httpConf absent) - Given a valid configuration - And without attribute "httpConf" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf' absent" - - Scenario: [log.json] (httpConf.level different) - Given a valid configuration - And with parameter "test" for attribute "httpConf.level" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [log.json] (httpConf.level vide) - Given a valid configuration - And with parameter "" for attribute "httpConf.level" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.level' absent" - - Scenario: [log.json] (httpConf.level absent) - Given a valid configuration - And without attribute "httpConf.level" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.level' absent" - - Scenario: [log.json] (httpConf.format different) - Given a valid configuration - And with parameter "test" for attribute "httpConf.format" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [log.json] (httpConf.format vide) - Given a valid configuration - And with parameter "" for attribute "httpConf.format" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.format' absent" - - Scenario: [log.json] (httpConf.format absent) - Given a valid configuration - And without attribute "httpConf.format" in log configuration - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mausvaise configuration pour les logs: 'httpConf.format' absent" - - Scenario: [cors.json] Contenu different - Given a valid configuration - And with parameter "127.0.0.1" for attribute "origin" in cors configuration - And with parameter "GET,POST" for attribute "methods" in cors configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - And the server log should not contain error - - Scenario: [cors.json] Contenu mauvais - Given a valid configuration - And with parameter "TEST" for attribute "methods" in cors configuration - When I test the configuration - Then the configuration analysis should give an exit code 0 - And the server log should not contain error - - Scenario: [projection.json] JSON invalide - Given a valid configuration - And a wrong JSON file "./projections/test.json" - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Mauvaise configuration: impossible de lire ou de parser le fichier de projection" - - Scenario: [projection.json] JSON vide - Given a valid configuration - And without attribute "projectionsList" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration des projections ne contient pas de liste" - - Scenario: [projection.json] projectionsList absent - Given a valid configuration - And without attribute "projectionsList" in "projection.json" projection - And with parameter "test" for attribute "projections" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration des projections ne contient pas de liste" - - Scenario: [projection.json] projectionsList different - Given a valid configuration - And with parameter "test" for attribute "projectionsList" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'attribut projectionsList de la configuration n'est pas un tableau" - - Scenario: [projection.json] projectionsList est un tableau vide - Given a valid configuration - And without attribute "projectionsList.[6]" in "projection.json" projection - And without attribute "projectionsList.[5]" in "projection.json" projection - And without attribute "projectionsList.[4]" in "projection.json" projection - And without attribute "projectionsList.[3]" in "projection.json" projection - And without attribute "projectionsList.[2]" in "projection.json" projection - And without attribute "projectionsList.[1]" in "projection.json" projection - And without attribute "projectionsList.[0]" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'attribut projectionsList de la configuration est un tableau vide" - - Scenario: [projection.json] une configuration de projection vide - Given a valid configuration - And without attribute "projectionsList.[0].id" in "projection.json" projection - And without attribute "projectionsList.[0].parameters" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration de la projection n'a pas d'id" - - Scenario: [projection.json] une configuration de projection sans id - Given a valid configuration - And without attribute "projectionsList.[0].id" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration de la projection n'a pas d'id" - - Scenario: [projection.json] une configuration de projection sans parametre - Given a valid configuration - And without attribute "projectionsList.[0].parameters" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration de la projection n'a pas de parametres" - - Scenario: [projection.json] une configuration de projection différentes - Given a valid configuration - And with parameter "test" for attribute "projectionsList.[0]" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration de la projection n'a pas d'id" - - Scenario: [projection.json] projection.id différent pour une projection inutilisée dans les topologies - Given a valid configuration - And with parameter "test" for attribute "projectionsList.[2].id" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [projection.json] projection.id différent pour une projection utilisée dans les topologies - Given a valid configuration - And with parameter "test" for attribute "projectionsList.[0].id" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - - Scenario: [projection.json] projection.id vide - Given a valid configuration - And with parameter "" for attribute "projectionsList.[2].id" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration de la projection n'a pas d'id" - - Scenario: [projection.json] projection.parameters différent - Given a valid configuration - And with parameter "test" for attribute "projectionsList.[0].parameters" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Impossible de charger la projection dans proj4" - - Scenario: [projection.json] projection.parameters vide - Given a valid configuration - And with parameter "" for attribute "projectionsList.[0].parameters" in "projection.json" projection - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La configuration de la projection n'a pas de parametres" - - Scenario: [operations] id différent - Given a valid configuration - And with parameter "test" for attribute "id" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation indiquee n'est pas disponible" - - Scenario: [operations] id vide - Given a valid configuration - And with parameter "" for attribute "id" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'id" - - Scenario: [operations] id absent - Given a valid configuration - And without attribute "id" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'id" - - Scenario: [operations] name différent - Given a valid configuration - And with parameter "test" for attribute "name" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [operations] name vide - Given a valid configuration - And with parameter "" for attribute "name" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'attribut name" - - Scenario: [operations] name absent - Given a valid configuration - And without attribute "name" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'attribut name" - - Scenario: [operations] description différent - Given a valid configuration - And with parameter "test" for attribute "description" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [operations] description vide - Given a valid configuration - And with parameter "" for attribute "description" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'attribut description" - - Scenario: [operations] description absent - Given a valid configuration - And without attribute "description" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'attribut description" - - Scenario: [operations] parameters différent - Given a valid configuration - And with parameter "test" for attribute "parameters" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Les parametres de l'operation ne sont pas dans un tableau" - - Scenario: [operations] parameters vide - Given a valid configuration - And with parameter "" for attribute "parameters" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'attribut parameters" - - Scenario: [operations] parameters absent - Given a valid configuration - And without attribute "parameters" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation ne contient pas d'attribut parameters" - - Scenario: [operations] parameters est un tableau vide - Given a valid configuration - And without attribute "parameters.[13]" in "route.json" operation - And without attribute "parameters.[12]" in "route.json" operation - And without attribute "parameters.[11]" in "route.json" operation - And without attribute "parameters.[10]" in "route.json" operation - And without attribute "parameters.[9]" in "route.json" operation - And without attribute "parameters.[8]" in "route.json" operation - And without attribute "parameters.[7]" in "route.json" operation - And without attribute "parameters.[6]" in "route.json" operation - And without attribute "parameters.[5]" in "route.json" operation - And without attribute "parameters.[4]" in "route.json" operation - And without attribute "parameters.[3]" in "route.json" operation - And without attribute "parameters.[2]" in "route.json" operation - And without attribute "parameters.[1]" in "route.json" operation - And without attribute "parameters.[0]" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le tableau des parametres est vide" - - Scenario: [operations] parameters est incomplet - Given a valid configuration - And without attribute "parameters.[0]" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le nombre de parametres presents n'est pas celui attendu" - - Scenario: [operations] parameters contient un element different - Given a valid configuration - And with parameter "test" for attribute "parameters.[0]" in "route.json" operation - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation précise un parametre qui n'est pas disponible" - - Scenario: [parameters] id différent - Given a valid configuration - And with parameter "test" for attribute "id" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "L'operation précise un parametre qui n'est pas disponible: intermediates" - - Scenario: [parameters] id vide - Given a valid configuration - And with parameter "" for attribute "id" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'id" - - Scenario: [parameters] id absent - Given a valid configuration - And without attribute "id" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'id" - - Scenario: [parameters] id deja utilise - Given a valid configuration - And with parameter "test" for attribute "id" in "start.json" parameter - And with parameter "test" for attribute "id" in "end.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre contenant l'id test est deja reference" - - Scenario: [parameters] name différent - Given a valid configuration - And with parameter "test" for attribute "name" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] name vide - Given a valid configuration - And with parameter "" for attribute "name" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut name" - - Scenario: [parameters] name absent - Given a valid configuration - And without attribute "name" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut name" - - Scenario: [parameters] description différent - Given a valid configuration - And with parameter "test" for attribute "description" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] description vide - Given a valid configuration - And with parameter "" for attribute "description" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut description" - - Scenario: [parameters] description absent - Given a valid configuration - And without attribute "description" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut description" - - Scenario: [parameters] required différent - Given a valid configuration - And with parameter "true" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] required invalide - Given a valid configuration - And with parameter "test" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre contient un attribut required mal configure" - - Scenario: [parameters] required vide - Given a valid configuration - And with parameter "" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut required" - - Scenario: [parameters] required absent - Given a valid configuration - And without attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut required" - - Scenario: [parameters] defaultValue différent (true donc probleme apres) - Given a valid configuration - And with parameter "true" for attribute "defaultValue" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas de valeur par defaut alors qu'il doit en avoir un" - - Scenario: [parameters] defaultValue invalide - Given a valid configuration - And with parameter "test" for attribute "defaultValue" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre contient un attribut defaultValue mal configure" - - Scenario: [parameters] defaultValue vide - Given a valid configuration - And with parameter "" for attribute "defaultValue" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut defaultValue" - - Scenario: [parameters] defaultValue absent - Given a valid configuration - And without attribute "defaultValue" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut defaultValue" - - Scenario: [parameters] example différent - Given a valid configuration - And with parameter "test" for attribute "example" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] example vide - Given a valid configuration - And with parameter "" for attribute "example" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Le parametre ne contient pas d'exemple" - - Scenario: [parameters] example absent - Given a valid configuration - And without attribute "example" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - Then the server log should contain "Le parametre ne contient pas d'exemple" - - Scenario: [parameters] type différent - Given a valid configuration - And with parameter "boolean" for attribute "type" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "La valeur du parametre est incorrecte par rapport à son type: boolean" - - Scenario: [parameters] type invalide - Given a valid configuration - And with parameter "test" for attribute "type" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le type du parametre est incorrect" - - Scenario: [parameters] type vide - Given a valid configuration - And with parameter "" for attribute "type" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut type" - - Scenario: [parameters] type absent - Given a valid configuration - And without attribute "type" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre ne contient pas d'attribut type" - - Scenario: [parameters] min différent et required true - Given a valid configuration - And with parameter "1" for attribute "min" in "intermediates.json" parameter - And with parameter "true" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] min différent et required false - Given a valid configuration - And with parameter "1" for attribute "min" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre min est incorrect: valeur superieure a 0" - - Scenario: [parameters] min a 0 et required true - Given a valid configuration - And with parameter "0" for attribute "min" in "intermediates.json" parameter - And with parameter "true" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre min est incorrect: valeur inferieure a 1" - - Scenario: [parameters] min invalide - Given a valid configuration - And with parameter "test" for attribute "min" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre min est incorrect: valeur non entiere" - - Scenario: [parameters] min vide - Given a valid configuration - And with parameter "" for attribute "min" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] min absent - Given a valid configuration - And without attribute "min" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] max différent - Given a valid configuration - And with parameter "1" for attribute "max" in "intermediates.json" parameter - And with parameter "true" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] max a 0 - Given a valid configuration - And with parameter "0" for attribute "max" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre max est incorrect: valeur inferieure a 1" - - Scenario: [parameters] max invalide - Given a valid configuration - And with parameter "test" for attribute "max" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre max est incorrect: valeur non entiere" - - Scenario: [parameters] max vide - Given a valid configuration - And with parameter "" for attribute "max" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] max absent - Given a valid configuration - And without attribute "max" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] max plus petit que min - Given a valid configuration - And with parameter "1" for attribute "max" in "intermediates.json" parameter - And with parameter "2" for attribute "min" in "intermediates.json" parameter - And with parameter "true" for attribute "required" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre max est incorrect: valeur inferieure au parametre min" - - Scenario: [parameters] explode différent (true) - Given a valid configuration - And with parameter "true" for attribute "explode" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] explode invalide - Given a valid configuration - And with parameter "test" for attribute "explode" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre explode est incorrect" - - Scenario: [parameters] explode vide - Given a valid configuration - And with parameter "" for attribute "explode" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] explode absent - Given a valid configuration - And without attribute "explode" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 0 - - Scenario: [parameters] style invalide alors que explode false - Given a valid configuration - And with parameter "test" for attribute "style" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre style est incorrect" - - Scenario: [parameters] style vide alors que explode false - Given a valid configuration - And with parameter "" for attribute "style" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre style n'est pas present alors que explode=false" - - Scenario: [parameters] style vide alors que explode false - Given a valid configuration - And without attribute "style" in "intermediates.json" parameter - When I test the configuration - Then the configuration analysis should give an exit code 1 - Then the server log should contain "Le parametre style n'est pas present alors que explode=false" - - Scenario: [osrm resource] resource absent - Given a valid configuration - And without attribute "resource" in "data-osm.resource" resource - When I test the configuration - Then the configuration analysis should give an exit code 1 - # TODO : avoir plusieurs ressources dans le dossier pour qu'une d'entre elles marche - # Then the configuration analysis should give an exit code 0 - # Then the server log should contain "Erreur lors de la lecture de la ressource" - - Scenario: [osrm resource] id different - Given a valid configuration - And with parameter "test" for attribute "resource.id" in "data-osm.resource" resource - When I test the configuration - Then the configuration analysis should give an exit code 0 diff --git a/test/functional/configuration/cucumber/features/support/steps.js b/test/functional/configuration/cucumber/features/support/steps.js index a72869a..ab6a7f8 100644 --- a/test/functional/configuration/cucumber/features/support/steps.js +++ b/test/functional/configuration/cucumber/features/support/steps.js @@ -3,6 +3,7 @@ const assert = require('assert'); After( function () { this.cleanTmpDirectory(); + // this.killChildProcess(); }); Given("I have loaded all my test configuration", function() { @@ -29,6 +30,14 @@ Given("with parameter {string} for attribute {string} in server configuration", this.modifyServerConfiguration(value, attribute, "", "server", "modify"); }); +Given("with parameter {string} for attribute {string} in service configuration", function(value, attribute) { + this.modifyServerConfiguration(value, attribute, "", "service", "modify"); +}); + +Given("without attribute {string} in service configuration", function(attribute) { + this.modifyServerConfiguration("", attribute, "", "service", "delete"); +}); + Given("without attribute {string} in server configuration", function(attribute) { this.modifyServerConfiguration("", attribute, "", "server", "delete"); }); @@ -49,12 +58,12 @@ Given("a wrong JSON file {string}", function(relativeFilePath) { this.createWrongJSONFile(relativeFilePath); }); -Given("with parameter {string} for attribute {string} in log configuration", function(value, attribute) { - this.modifyServerConfiguration(value, attribute, "", "log", "modify"); +Given("with parameter {string} for attribute {string} in service log configuration", function(value, attribute) { + this.modifyServerConfiguration(value, attribute, "", "log-service", "modify"); }); -Given("without attribute {string} in log configuration", function(attribute) { - this.modifyServerConfiguration("", attribute, "", "log", "delete"); +Given("without attribute {string} in service log configuration", function(attribute) { + this.modifyServerConfiguration("", attribute, "", "log-service", "delete"); }); Given("with parameter {string} for attribute {string} in cors configuration", function(value, attribute) { @@ -131,7 +140,7 @@ Then("the server log should not contain {string}", function(message) { assert.equal(this.findInServerLog(message), false); }); -Then("the server log should not contain error", function() { +Then("the command log should not contain error", function() { assert.equal(this._stderr, ""); }); diff --git a/test/functional/configuration/cucumber/features/support/world.js b/test/functional/configuration/cucumber/features/support/world.js index eb8cf2f..50a8c30 100644 --- a/test/functional/configuration/cucumber/features/support/world.js +++ b/test/functional/configuration/cucumber/features/support/world.js @@ -44,11 +44,17 @@ class road2World { // Contenu du server.json pour le test en cours this._serverConf = {}; + // Contenu du service.json pour le test en cours + this._serviceConf = {}; + // Boolean pour savoir si le fichier de conf sera lisible ou pas this._serverReadable = true; - // Contenu du log4js.json pour le test en cours - this._logConf = {}; + // Contenu du log4js-administration.json pour le test en cours + this._logAdminConf = {}; + + // Contenu du log4js-service.json pour le test en cours + this._logServiceConf = {}; // Contenu du cors.json pour le test en cours this._corsConf = {}; @@ -68,6 +74,9 @@ class road2World { // Dossier temporaire pour le test en cours this._tmpDirConf = ""; + // Instance de childProcess pour le test en cours + this._childProcess; + // Code de retour de Road2 pour le test en cours this._code; @@ -152,22 +161,36 @@ class road2World { throw "Can't parse server conf: " + error; } - // Lecture du log4js.json + // Lecture du log4js-administration.json + try { + this._logAdminConf = JSON.parse(fs.readFileSync(this._serverConf.administration.logs.configuration)); + } catch(error) { + throw "Can't parse log4js admin conf: " + error; + } + + // Lecture du service.json try { - this._logConf = JSON.parse(fs.readFileSync(this._serverConf.application.logs.configuration)); + this._serviceConf = JSON.parse(fs.readFileSync(this._serverConf.administration.services[0].configuration)); + } catch(error) { + throw "Can't parse log conf: " + error; + } + + // Lecture du log4js-service.json + try { + this._logServiceConf = JSON.parse(fs.readFileSync(this._serviceConf.application.logs.configuration)); } catch(error) { throw "Can't parse log conf: " + error; } // Lecture du cors.json try { - this._corsConf = JSON.parse(fs.readFileSync(this._serverConf.application.network.cors.configuration)); + this._corsConf = JSON.parse(fs.readFileSync(this._serviceConf.application.network.cors.configuration)); } catch(error) { throw "Can't parse cors conf: " + error; } // Lecture des projections - let projDir = this._serverConf.application.projections.directory; + let projDir = this._serviceConf.application.projections.directory; try { projDirFiles = fs.readdirSync(projDir); @@ -184,7 +207,7 @@ class road2World { } // Lecture des operations - let operationsDir = this._serverConf.application.operations.directory; + let operationsDir = this._serviceConf.application.operations.directory; try { operationsDirFiles = fs.readdirSync(operationsDir); @@ -201,7 +224,7 @@ class road2World { } // Lecture des parametres - let parametersDir = this._serverConf.application.operations.parameters.directory; + let parametersDir = this._serviceConf.application.operations.parameters.directory; try { parametersDirFiles = fs.readdirSync(parametersDir); @@ -218,7 +241,7 @@ class road2World { } // Lecture des ressources - let resourceDir = this._serverConf.application.resources.directories; + let resourceDir = this._serviceConf.application.resources.directories; // Pour chaque dossier, on récupère l'ensemble des fichiers for (let i = 0; i < resourceDir.length; i++) { @@ -248,29 +271,35 @@ class road2World { // 3. On modifie la configuration pour qu'elle puisse être copiée dans l'espace temporaire // mais elle pourra de nouveau être modifiée dans la suite du scénario - // Emplacement du log4js.json - this._serverConf.application.logs.configuration = path.join(this._tmpDirConf, "log4js.json"); + // Emplacement du service.json + this._serverConf.administration.services[0].configuration = path.join(this._tmpDirConf, "service.json"); + + // Emplacement du log4js-administration.json + this._serverConf.administration.logs.configuration = path.join(this._tmpDirConf, "log4js-administration.json"); + + // Emplacement du log4js-service.json + this._serviceConf.application.logs.configuration = path.join(this._tmpDirConf, "log4js-service.json"); // Emplacement des ressources - this._serverConf.application.resources.directories = newResourcesDirectories; + this._serviceConf.application.resources.directories = newResourcesDirectories; // Emplacement des projections let curProjDir = path.join(this._tmpDirConf, "projections"); - this._serverConf.application.projections.directory = curProjDir; + this._serviceConf.application.projections.directory = curProjDir; if (!fs.existsSync(curProjDir)) { fs.mkdirSync(curProjDir, {recursive: true, mode: "766"}); } // Emplacement des operations let curOperationsDir = path.join(this._tmpDirConf, "operations"); - this._serverConf.application.operations.directory = curOperationsDir; + this._serviceConf.application.operations.directory = curOperationsDir; if (!fs.existsSync(curOperationsDir)) { fs.mkdirSync(curOperationsDir, {recursive: true, mode: "766"}); } // Emplacement des parameters let curParametersDir = path.join(this._tmpDirConf, "parameters"); - this._serverConf.application.operations.parameters.directory = curParametersDir; + this._serviceConf.application.operations.parameters.directory = curParametersDir; if (!fs.existsSync(curParametersDir)) { fs.mkdirSync(curParametersDir, {recursive: true, mode: "766"}); } @@ -292,8 +321,12 @@ class road2World { // 1. On commence par déterminer sur quelle élément de la configuration on va faire des modifications if (configurationType === "server") { modification = this._serverConf; - } else if (configurationType === "log") { - modification = this._logConf; + } else if (configurationType === "log-admin") { + modification = this._logAdminConf; + } else if (configurationType === "service") { + modification = this._serviceConf; + } else if (configurationType === "log-service") { + modification = this._logServiceConf; } else if (configurationType === "cors") { modification = this._corsConf; } else if (configurationType === "projection") { @@ -489,9 +522,21 @@ class road2World { } try { - fs.writeFileSync(path.join(this._tmpDirConf, "log4js.json"), JSON.stringify(this._logConf)); + fs.writeFileSync(path.join(this._tmpDirConf, "log4js-administration.json"), JSON.stringify(this._logAdminConf)); } catch(error) { - throw "Can't write log4js.json : " + error; + throw "Can't write log4js-administration.json : " + error; + } + + try { + fs.writeFileSync(path.join(this._tmpDirConf, "service.json"), JSON.stringify(this._serviceConf)); + } catch(error) { + throw "Can't write service.json : " + error; + } + + try { + fs.writeFileSync(path.join(this._tmpDirConf, "log4js-service.json"), JSON.stringify(this._logServiceConf)); + } catch(error) { + throw "Can't write log4js-service.json : " + error; } try { @@ -562,21 +607,21 @@ class road2World { // On lance l'analyse de la conf par Road2 return new Promise ( (resolve, reject) => { - const command = spawn("node", options); + this._childProcess = spawn("node", options); - command.stdout.on("data", (data) => { + this._childProcess.stdout.on("data", (data) => { this._stdout += data.toString(); }); - command.stderr.on("data", (data) => { + this._childProcess.stderr.on("data", (data) => { this._stderr += data.toString(); }); - command.on("error", (err) => { + this._childProcess.on("error", (err) => { reject(err); }); - command.on("close", (code) => { + this._childProcess.on("close", (code) => { this._code = code; resolve(); }); @@ -592,7 +637,7 @@ class road2World { if (code === this._code) { return true; } else { - return false; + return this._code; } } @@ -626,6 +671,18 @@ class road2World { } } + + // Gestion du processus enfant + // killChildProcess() { + + // if (this._childProcess.exitCode === null) { + // // On va killer le process pour passer au test suivant + // this._childProcess.kill(9); + // } else { + // // Le processus est déjà mort, donc il n'y a rien à faire + // } + + // } } diff --git a/test/functional/request/cucumber/configurations/local-admin.json b/test/functional/request/cucumber/configurations/local-admin.json new file mode 100644 index 0000000..ed251b6 --- /dev/null +++ b/test/functional/request/cucumber/configurations/local-admin.json @@ -0,0 +1,17 @@ +{ + "url": "localhost", + "port": 8079, + "protocol": "http", + "apisUrl": { + "admin": { + "1.0.0": { + "health": "/admin/1.0.0/health", + "version": "/admin/1.0.0/version" + } + } + }, + "defaultParameters": [], + "alternativeParameters" : { + "url" : "127.0.0.1" + } +} \ No newline at end of file diff --git a/test/functional/request/cucumber/configurations/local.json b/test/functional/request/cucumber/configurations/local-service.json similarity index 90% rename from test/functional/request/cucumber/configurations/local.json rename to test/functional/request/cucumber/configurations/local-service.json index 024b120..9ebb29b 100644 --- a/test/functional/request/cucumber/configurations/local.json +++ b/test/functional/request/cucumber/configurations/local-service.json @@ -10,12 +10,6 @@ "nearest": "/simple/1.0.0/nearest", "getcapabilities": "/simple/1.0.0/getcapabilities" } - }, - "admin": { - "1.0.0": { - "health": "/admin/1.0.0/health", - "version": "/admin/1.0.0/version" - } } }, "defaultParameters": [ diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index 6c5a8fb..209c80d 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -3,7 +3,7 @@ Feature: Road2 with data Tests fonctionnels de Road2 prenant en compte la donnée Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-admin.json" Scenario Outline: [admin/1.0.0] Une route qui n'existe pas Given an "" request on "/admin/1.0.0/test" diff --git a/test/functional/request/cucumber/features/req-data-bduni-osrm.feature b/test/functional/request/cucumber/features/req-data-bduni-osrm.feature index f43685e..02f178b 100644 --- a/test/functional/request/cucumber/features/req-data-bduni-osrm.feature +++ b/test/functional/request/cucumber/features/req-data-bduni-osrm.feature @@ -2,7 +2,7 @@ Feature: Road2 with Bduni data via OSRM Tests fonctionnels de Road2 prenant en compte la donnée Bduni via le moteur OSRM Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-service.json" Scenario Outline: [] [simple/1.0.0] Route normale Given an "" request on operation "route" in api "simple" "1.0.0" diff --git a/test/functional/request/cucumber/features/req-data-bduni-pgr.feature b/test/functional/request/cucumber/features/req-data-bduni-pgr.feature index 4b87879..53ac3e1 100644 --- a/test/functional/request/cucumber/features/req-data-bduni-pgr.feature +++ b/test/functional/request/cucumber/features/req-data-bduni-pgr.feature @@ -2,7 +2,7 @@ Feature: Road2 with Bduni data via PGR Tests fonctionnels de Road2 prenant en compte la donnée Bduni via PGR Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-service.json" Scenario Outline: [] [simple/1.0.0] Route normale Given an "" request on operation "route" in api "simple" "1.0.0" diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature index 870b1d9..6c2dbf5 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-common.feature @@ -2,7 +2,7 @@ Feature: Road2 Tests fonctionnels de Road2 sur les requêtes de l'API simple 1.0.0 Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-service.json" Scenario Outline: [] Route principale Given an "" request on "/" diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature index 41d1d95..225bdbb 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature @@ -2,7 +2,7 @@ Feature: Road2-OSRM Tests fonctionnels complémentaires de Road2 sur OSRM Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-service.json" Scenario Outline: [] Route sur l'API simple 1.0.0 diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index aa73d59..c2c06a3 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -2,7 +2,7 @@ Feature: Road2-PGR Tests fonctionnels complémentaires de Road2 via PGRouting Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-service.json" Scenario Outline: [] Route sur l'API simple 1.0.0 Given an "" request on operation "route" in api "simple" "1.0.0" diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature index 9197368..fb704ae 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature @@ -2,7 +2,7 @@ Feature: Road2-SMARTROUTING Tests fonctionnels complémentaires de Road2 via SmartRouting Background: - Given I have loaded all my test configuration in "../../configurations/local.json" + Given I have loaded all my test configuration in "../../configurations/local-service.json" Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource smartpgr avec appel à la source smartrouting Given an "" request on operation "isochrone" in api "simple" "1.0.0" diff --git a/test/integration/mocha/apis/integrationApiManager.js b/test/integration/mocha/apis/integrationApiManager.js index 18de119..3df128f 100644 --- a/test/integration/mocha/apis/integrationApiManager.js +++ b/test/integration/mocha/apis/integrationApiManager.js @@ -31,10 +31,10 @@ describe('Test de la classe ApisManager', function() { }); - describe('Test de la fonction loadAPISDirectory()', function() { + describe('Test de la fonction loadApiDirectory()', function() { - it('loadAPISDirectory() avec les bons parametres', function() { - assert.equal(apisManager.loadAPISDirectory(app, "../../../test/integration/mocha/config/apis/", ""), true); + it('loadApiDirectory() avec les bons parametres', function() { + assert.equal(apisManager.loadApiDirectory(app, "../../../test/integration/mocha/config/apis/", ""), true); }); }); diff --git a/test/integration/mocha/base/integrationBaseManager.js b/test/integration/mocha/base/integrationBaseManager.js index 73e2943..53fcd01 100644 --- a/test/integration/mocha/base/integrationBaseManager.js +++ b/test/integration/mocha/base/integrationBaseManager.js @@ -18,8 +18,8 @@ describe('Test de la classe BaseManager', function() { describe('Test du constructeur et des getters/setters', function() { - it('Get listOfVerifiedDbConfig', function() { - assert.deepEqual(baseManager.listOfVerifiedDbConfig, new Array()); + it('Get loadedBaseConfiguration', function() { + assert.deepEqual(baseManager.loadedBaseConfiguration, new Array()); }); it('Get baseCatalog', function() { @@ -30,17 +30,22 @@ describe('Test de la classe BaseManager', function() { describe('Verifications des configurations', function() { - it('checkBase()', function() { - assert.equal(baseManager.checkBase(configuration), true); - assert.deepEqual(baseManager.listOfVerifiedDbConfig, [configuration]); + it('checkBaseConfiguration()', async function() { + let response = await baseManager.checkBaseConfiguration(configuration); + assert.equal(response, true); + }); + + it('saveCheckedBaseConfiguration()', function() { + baseManager.saveCheckedBaseConfiguration(configuration); + assert.deepEqual(baseManager._checkedBaseConfiguration, [configuration]); }); }); describe('Creation d\'une base', function() { - it('createBase()', function() { - let newBase = baseManager.createBase(configuration); + it('loadBaseConfiguration()', function() { + let newBase = baseManager.loadBaseConfiguration(configuration); // TODO: comprendre pourquoi le assert qui suit ne marche pas // assert.deepEqual(baseManager.baseCatalog[configuration], referenceBase); assert.equal(newBase.connected, false); diff --git a/test/integration/mocha/service/integrationService.js b/test/integration/mocha/service/integrationService.js index ca8aebd..bd4a7e3 100644 --- a/test/integration/mocha/service/integrationService.js +++ b/test/integration/mocha/service/integrationService.js @@ -82,7 +82,7 @@ describe('Test de la classe Service', function() { describe('Test de createServer() et stopServer()', function() { const apisManager = sinon.mock(ApisManager); - apisManager.loadAPISDirectory = sinon.stub().returns(true); + apisManager.loadApiDirectory = sinon.stub().returns(true); service._apisManager = apisManager; const serverManager = sinon.mock(ServerManager); diff --git a/test/integration/readme.md b/test/integration/readme.md index e732e12..b60d3b8 100644 --- a/test/integration/readme.md +++ b/test/integration/readme.md @@ -9,7 +9,7 @@ docker-compose exec road2 npm run itest C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les classes qui dépendent d'une autre pour fonctionner. On testera donc les classes suivantes dans l'ordre indiqué: - Premier niveau: - - apiManager (ExpressJS, api, log4js) + - apisManager (ExpressJS, api, log4js) - baseManager (base, log4js) - looseConstraint (constraint) - line (geometry, proj4, polyline) @@ -60,7 +60,7 @@ Huitième niveau: - resourceManager (osrmResource, pgrResource, sourceManager, operationManager, log4js) Neuvième niveau: - - service (apiManager, resourceManager, sourceManager, operationManager, baseManager, topologyManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) + - service (apisManager, resourceManager, sourceManager, operationManager, baseManager, topologyManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) Autres: - road2.js diff --git a/test/unit/mocha/geography/testsProjectionManager.js b/test/unit/mocha/geography/testsProjectionManager.js index 5343721..1173859 100644 --- a/test/unit/mocha/geography/testsProjectionManager.js +++ b/test/unit/mocha/geography/testsProjectionManager.js @@ -13,8 +13,64 @@ describe('Test de la classe ProjectionManager', function() { let projManager = new ProjectionManager(); - it('Get listOfProjectionId', function() { - assert.deepEqual(projManager.listOfProjectionId, new Array()); + it('Get loadedProjectionId', function() { + assert.deepEqual(projManager.loadedProjectionId, new Array()); + }); + + }); + + describe('Vérification d\'une configuration de projection', function() { + + let projManager = new ProjectionManager(); + let configuration = { + "id": "EPSG:4326", + "parameters": "+proj=longlat +datum=WGS84 +no_defs" + }; + + it('checkProjectionConfiguration()', function() { + assert.equal(projManager.checkProjectionConfiguration(configuration), true); + }); + + }); + + describe('Vérification d\'un fichier de projections', function() { + + let projManager = new ProjectionManager(); + let file = "/home/docker/app/test/unit/mocha/config/projections/projection.json"; + + it('checkProjectionFile()', function() { + assert.equal(projManager.checkProjectionFile(file), true); + assert.equal(projManager.isChecked("EPSG:4326"), true); + assert.equal(projManager.isChecked("EPSG:2154"), true); + assert.equal(projManager.isChecked("EPSG:2155"), false); + }); + + }); + + describe('Vérification d\'un dossier de projections', function() { + + let projManager = new ProjectionManager(); + let directory = "/home/docker/app/test/unit/mocha/config/projections/"; + + it('checkProjectionDirectory()', function() { + assert.equal(projManager.checkProjectionDirectory(directory), true); + assert.equal(projManager.isChecked("EPSG:4326"), true); + assert.equal(projManager.isChecked("EPSG:2154"), true); + assert.equal(projManager.isChecked("EPSG:2155"), false); + }); + + }); + + describe('Test de isChecked', function() { + + let projManager = new ProjectionManager(); + let directory = "/home/docker/app/test/unit/mocha/config/projections/"; + + it('isChecked()', function() { + projManager.checkProjectionDirectory(directory); + assert.equal(projManager.isChecked("EPSG:4326"), true); + assert.equal(projManager.isChecked("EPSG:2154"), true); + assert.equal(projManager.isChecked("EPSG:2155"), false); }); }); @@ -27,9 +83,9 @@ describe('Test de la classe ProjectionManager', function() { "parameters": "+proj=longlat +datum=WGS84 +no_defs" }; - it('loadProjection()', function() { - assert.equal(projManager.loadProjection(configuration), true); - assert.deepEqual(projManager.listOfProjectionId, ["EPSG:4326"]); + it('loadProjectionConfiguration()', function() { + assert.equal(projManager.loadProjectionConfiguration(configuration), true); + assert.deepEqual(projManager.loadedProjectionId, ["EPSG:4326"]); }); }); @@ -41,9 +97,9 @@ describe('Test de la classe ProjectionManager', function() { it('loadProjectionFile()', function() { assert.equal(projManager.loadProjectionFile(file), true); - assert.equal(projManager.isAvailableById("EPSG:4326"), true); - assert.equal(projManager.isAvailableById("EPSG:2154"), true); - assert.equal(projManager.isAvailableById("EPSG:2155"), false); + assert.equal(projManager.isAvailable("EPSG:4326"), true); + assert.equal(projManager.isAvailable("EPSG:2154"), true); + assert.equal(projManager.isAvailable("EPSG:2155"), false); }); }); @@ -55,23 +111,23 @@ describe('Test de la classe ProjectionManager', function() { it('loadProjectionDirectory()', function() { assert.equal(projManager.loadProjectionDirectory(directory), true); - assert.equal(projManager.isAvailableById("EPSG:4326"), true); - assert.equal(projManager.isAvailableById("EPSG:2154"), true); - assert.equal(projManager.isAvailableById("EPSG:2155"), false); + assert.equal(projManager.isAvailable("EPSG:4326"), true); + assert.equal(projManager.isAvailable("EPSG:2154"), true); + assert.equal(projManager.isAvailable("EPSG:2155"), false); }); }); - describe('Test de isAvailableById', function() { + describe('Test de isAvailable', function() { let projManager = new ProjectionManager(); let directory = "/home/docker/app/test/unit/mocha/config/projections/"; - it('isAvailableById()', function() { + it('isAvailable()', function() { projManager.loadProjectionDirectory(directory); - assert.equal(projManager.isAvailableById("EPSG:4326"), true); - assert.equal(projManager.isAvailableById("EPSG:2154"), true); - assert.equal(projManager.isAvailableById("EPSG:2155"), false); + assert.equal(projManager.isAvailable("EPSG:4326"), true); + assert.equal(projManager.isAvailable("EPSG:2154"), true); + assert.equal(projManager.isAvailable("EPSG:2155"), false); }); }); From 980987d956f35d9f3efd775073cdf59722034edc Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Fri, 21 Oct 2022 15:09:38 +0200 Subject: [PATCH 08/93] feature(valhalla): Add support for Valhalla for routes and isochrones --- src/js/geometry/line.js | 12 +- src/js/resources/resourceManager.js | 5 +- src/js/resources/valhallaResource.js | 151 +++++ src/js/sources/sourceManager.js | 88 ++- src/js/sources/valhallaSource.js | 580 ++++++++++++++++++ src/js/utils/storageManager.js | 29 +- .../configurations/local-service.json | 17 +- .../features/req-simple-1.0.0-pgr.feature | 208 +++---- .../req-simple-1.0.0-valhalla.feature | 52 ++ .../resources/integrationValhallaResource.js | 104 ++++ .../sources/integrationValhallaSource.js | 135 ++++ 11 files changed, 1266 insertions(+), 115 deletions(-) create mode 100644 src/js/resources/valhallaResource.js create mode 100644 src/js/sources/valhallaSource.js create mode 100644 test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature create mode 100644 test/integration/mocha/resources/integrationValhallaResource.js create mode 100644 test/integration/mocha/sources/integrationValhallaSource.js diff --git a/src/js/geometry/line.js b/src/js/geometry/line.js index 87fb1ca..a2076dd 100644 --- a/src/js/geometry/line.js +++ b/src/js/geometry/line.js @@ -25,9 +25,10 @@ module.exports = class Line extends Geometry { * @param {object} geom - Géométrie * @param {string} format - Format de la geom (geojson, polyline) * @param {string} projection - Id de la projection utilisée (EPSG:4326) + * @param {integer} polylinePrecision - Précision de l'encodage en polyline en entrée (défaut : 5) * */ - constructor(geom, format, projection) { + constructor(geom, format, projection, polylinePrecision = 5) { super("polyline", projection); @@ -37,6 +38,9 @@ module.exports = class Line extends Geometry { // Format de géométrie (geojson, polyline...) this._format = format; + if (polylinePrecision != 5) { + this._geom = polyline.encode(polyline.decode(geom, polylinePrecision)); + } } /** @@ -131,11 +135,11 @@ module.exports = class Line extends Geometry { this._geom = tmpGeom; this.projection = projection; - + return true; } - + } else { // il n'y a rien à faire return true; @@ -167,7 +171,7 @@ module.exports = class Line extends Geometry { return {}; } - // reprojection + // reprojection let reprojectedCoordinates = new Array(); for (let i = 0; i < geojson.coordinates.length; i++) { diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index 6a20a39..b6a685d 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -5,6 +5,7 @@ const path = require('path'); const osrmResource = require('../resources/osrmResource'); const pgrResource = require('../resources/pgrResource'); const smartpgrResource = require('../resources/smartpgrResource'); +const valhallaResource = require('../resources/valhallaResource'); const log4js = require('log4js'); // Création du LOGGER @@ -31,7 +32,7 @@ module.exports = class resourceManager { this._resource = {}; // Liste des types de ressource gérées par le manager - this._availableResourceTypes = ["pgr", "smartpgr","osrm"]; + this._availableResourceTypes = ["pgr", "smartpgr","osrm","valhalla"]; // Manager de topology this._topologyManager = topologyManager; @@ -417,6 +418,8 @@ module.exports = class resourceManager { resource = new pgrResource(resourceJsonObject, resourceOperationHash); } else if (resourceJsonObject.resource.type === "smartpgr") { resource = new smartpgrResource(resourceJsonObject, resourceOperationHash); + } else if (resourceJsonObject.resource.type === "valhalla") { + resource = new valhallaResource(resourceJsonObject, resourceOperationHash); } else { LOGGER.error("Type de la ressource inconnue"); return false; diff --git a/src/js/resources/valhallaResource.js b/src/js/resources/valhallaResource.js new file mode 100644 index 0000000..f470787 --- /dev/null +++ b/src/js/resources/valhallaResource.js @@ -0,0 +1,151 @@ +'use strict'; + +const Resource = require('./resource'); + +/** +* +* @class +* @name valhallaResource +* @description Classe modélisant une ressource Valhalla. +* +*/ + +module.exports = class valhallaResource extends Resource { + + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe valhallaResource + * @param {json} resourceJsonObject - Description JSON de la ressource + * @param {object} operations - Objet contenant des instances de classes filles de ResourceOperation + * + */ + constructor(resourceJsonObject, operations) { + + // Constructeur parent + super(resourceJsonObject.resource.id,resourceJsonObject.resource.type, resourceJsonObject.resource.resourceVersion, operations); + + // Stockage de la configuration + this._configuration = resourceJsonObject.resource; + + // Correspondance entre profile/optimization et sourceId + this._linkedSource = {}; + + // Instanciation de la correspondance entre profile/optimization et sourceId + // et instanciation du profile et de l'optimisation par défaut + for (let i=0; i < this._configuration.sources.length; i++) { + + /* TODO: Il serait mieux, dans le futur, d'avoir un nouveau type de ressource, dédiée à l'isochrone. */ + const currentSourceOptimization = this._configuration.sources[i].cost.optimization; + let linkedId = ''; + if (operations["isochrone"]) { + if (currentSourceOptimization === "fastest") { + linkedId = this._configuration.sources[i].cost.profile + "time"; + } else if (currentSourceOptimization === "shortest") { + linkedId = this._configuration.sources[i].cost.profile + "distance"; + } else { + /* TODO: À repenser. */ + } + this._linkedSource[linkedId] = this._configuration.sources[i].id; + } + if (operations["route"]) { + linkedId = this._configuration.sources[i].cost.profile + this._configuration.sources[i].cost.optimization; + this._linkedSource[linkedId] = this._configuration.sources[i].id; + } + } + + // Attribut des voies + // Par défaut, OSRM ne renvoit que le nom des voies empruntées. + this._waysAttributes = new Array(); + this._waysAttributes.push("name"); + + } + + /** + * + * @function + * @name get configuration + * @description Récupérer la configuration de la ressource + * + */ + get configuration () { + return this._configuration; + } + + /** + * + * @function + * @name get linkedSource + * @description Récupérer la correspondance entre profile/optimization et sourceId de la ressource + * + */ + get linkedSource () { + return this._linkedSource; + } + + /** + * + * @function + * @name get waysAttributes + * @description Récupérer la liste des attributs disponibles pour les voies empruntées. + * + */ + get waysAttributes () { + return this._waysAttributes; + } + + /** + * + * @function + * @name isWayAttributeAvailable + * @description Permet de savoir si un attribut est disponible pour cette ressource. + * @param {string} attr - Attribut à vérifier + * + */ + isWayAttributeAvailable (attr) { + + if (this._waysAttributes.length !== 0) { + for (let i=0; i < this._waysAttributes.length; i++) { + if (this._waysAttributes[i] === attr) { + return true; + } else { + // on continue + } + } + } else { + return false; + } + + return false; + } + + /** + * + * @function + * @name getSourceIdFromRequest + * @description Récupérer l'id de la source concernée par la requête. + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {Request} request - Objet Request ou ou dérivant de la classe Request + * @return {string} Id de la source concernée par la requête + * + */ + getSourceIdFromRequest (request) { + + const currentOperation = request.operation; + let source = ""; + /* TODO: Pour le moment, c'est un contrôle en dur sur le type de l'opération. Il serait mieux de revoir cette façon de voir (avoir peut-être un catalogue de correspondance ? Maybe..). */ + if (currentOperation === "isochrone") { + source = request.profile + request.costType; + } else { + source = request.profile + request.optimization; + } + if (this._linkedSource[source]) { + return this._linkedSource[source]; + } else { + return null; + } + } + +} diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index 83fe2a2..86d365d 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -6,6 +6,7 @@ const errorManager = require('../utils/errorManager'); const osrmSource = require('../sources/osrmSource'); const pgrSource = require('../sources/pgrSource'); const smartroutingSource = require('../sources/smartroutingSource'); +const valhallaSource = require('../sources/valhallaSource'); const log4js = require('log4js'); // Création du LOGGER @@ -209,7 +210,7 @@ module.exports = class sourceManager { available = true; LOGGER.info("Source pgrouting."); - // On vérifie que le module pg est disponible + // On vérifie que le module pg est disponible try { let { poolTest } = require('pg'); } catch(error) { @@ -242,6 +243,52 @@ module.exports = class sourceManager { // On va voir si c'est un autre type. } //------ SMARTROUTING + //------ VALHALLA + if (sourceJsonObject.type === "valhalla") { + available = true; + LOGGER.info("Source valhalla."); + + let operationFound = false; + + // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service + if (operationManager.verifyAvailabilityOperation("route")) { + // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource + if (operationManager.isAvailableInTable("route", resourceOperationTable)) { + operationFound = true; + } else { + // on continue pour voir la suite + } + } else { + // on continue pour voir la suite + } + + // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service + if (operationManager.verifyAvailabilityOperation("isochrone")) { + // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource + if (operationManager.isAvailableInTable("isochrone", resourceOperationTable)) { + operationFound = true; + } else { + // on continue pour voir la suite + } + } else { + // on continue pour voir la suite + } + + if (!operationFound) { + LOGGER.error("Le service ne propose pas d'operations disponibles pour ce type de source (ex. route, isochrone), il n'est donc pas possible de charger cette source."); + return false; + } + + if (!this.checkSourceValhalla(sourceJsonObject)) { + LOGGER.error("Erreur lors de la verification de la source valhalla."); + return false; + } else { + // il n'y a eu aucun problème, la ressource est correctement configurée. + } + } else { + // On va voir si c'est un autre type. + } + //------ VALHALLA // Si ce n'est aucun type valide, on renvoie une erreur. if (!available) { @@ -396,7 +443,7 @@ module.exports = class sourceManager { * * @function * @name checkSourceSmartrouting - * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source pgr. + * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source smartrouting. * @param {json} sourceJsonObject - Description JSON de la source * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur * @@ -405,7 +452,38 @@ module.exports = class sourceManager { checkSourceSmartrouting(sourceJsonObject) { LOGGER.info("Verification de la source smartrouting..."); - + + // Storage + if (!sourceJsonObject.storage) { + LOGGER.error("La ressource contient une source sans stockage."); + return false; + } else { + if (!storageManager.checkJsonStorage(sourceJsonObject.storage)) { + LOGGER.error("Stockage de la source incorrect."); + return false; + } else { + // Normalement, il n'y a plus rien à faire car la fonction checkDuplicationSource() vérifie déjà que la source n'est pas dupliquée + } + } + + LOGGER.info("Fin de la verification de la source smartrouting."); + return true; + } + + /** + * + * @function + * @name checkSourceValhalla + * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source valhalla. + * @param {json} sourceJsonObject - Description JSON de la source + * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * + */ + + checkSourceValhalla(sourceJsonObject) { + + LOGGER.info("Verification de la source smartrouting..."); + // Storage if (!sourceJsonObject.storage) { LOGGER.error("La ressource contient une source sans stockage."); @@ -418,7 +496,7 @@ module.exports = class sourceManager { // Normalement, il n'y a plus rien à faire car la fonction checkDuplicationSource() vérifie déjà que la source n'est pas dupliquée } } - + LOGGER.info("Fin de la verification de la source smartrouting."); return true; } @@ -543,6 +621,8 @@ module.exports = class sourceManager { } else if (sourceJsonObject.type === "smartrouting") { // smartrouting n'utilise pas la topologie définie dans la conf source = new smartroutingSource(sourceJsonObject); + } else if (sourceJsonObject.type === "valhalla") { + source = new valhallaSource(sourceJsonObject, topology); } else { // On va voir si c'est un autre type. LOGGER.error("Le type de la source est inconnu"); diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js new file mode 100644 index 0000000..dd86535 --- /dev/null +++ b/src/js/sources/valhallaSource.js @@ -0,0 +1,580 @@ +'use strict'; + +const Source = require('./source'); +const RouteResponse = require('../responses/routeResponse'); +const IsochroneResponse = require('../responses/isochroneResponse'); +const Route = require('../responses/route'); +const Portion = require('../responses/portion'); +const Line = require('../geometry/line'); +const Point = require('../geometry/point'); +const Polygon = require('../geometry/polygon'); +const Step = require('../responses/step'); +const Distance = require('../geography/distance'); +const Duration = require('../time/duration'); +const errorManager = require('../utils/errorManager'); +const { exec } = require('child_process'); + +// Création du LOGGER +const log4js = require('log4js'); +const LOGGER = log4js.getLogger("VALHALLASOURCE"); + +/** +* +* @class +* @name valhallaSource +* @description Classe modélisant une source pgRouting. +* +*/ +module.exports = class valhallaSource extends Source { + /** + * + * @function + * @name constructor + * @description Constructeur de la classe valhallaSource + * @param{json} sourceJsonObject - Description de la source + * @param{topology} topology - Instance de la classe Topology + * + */ + constructor(sourceJsonObject, topology) { + + // Constructeur parent + super(sourceJsonObject.id, "valhalla", topology); + + // Stockage de la configuration + this._configuration = sourceJsonObject; + + } + + /** + * + * @function + * @name get configuration + * @description Récupérer la configuration de la source + * + */ + get configuration () { + return this._configuration; + } + + /** + * + * @function + * @name set configuration + * @description Attribuer la configuration de la source + * @param {json} conf - Description de la source en json + * + */ + set configuration (conf) { + this._configuration = conf; + } + + /** + * + * @function + * @name connect + * @description Connection + * + */ + async connect() { + this._connected = true; + } + + /** + * + * @function + * @name disconnect + * @description Déconnection + * + */ + async disconnect() { + this._connected = false; + } + + /** + * + * @function + * @name computeRequest + * @description Traiter une requête. + * Ce traitement est placé ici car c'est la source qui sait quel moteur est concernée par la requête. + * @param {Request} request - Objet Request ou dérivant de la classe Request + * @return {Promise} + * + */ + computeRequest (request) { + + LOGGER.debug("computeRequest()"); + + let valhallaRequest = {}; + + if (request.operation === "route") { + + LOGGER.debug("operation request is route"); + + const coordinatesTable = new Array(); + let constraints = new Array(); + + if (request.type === "routeRequest") { + + LOGGER.debug("type of request is routeRequest"); + + // Coordonnées + // start + coordinatesTable.push(request.start.getCoordinatesIn(this.topology.projection)); + // intermediates + if (request.intermediates.length !== 0) { + for (let i = 0; i < request.intermediates.length; i++) { + coordinatesTable.push(request.intermediates[i].getCoordinatesIn(this.topology.projection)); + } + } + // end + coordinatesTable.push(request.end.getCoordinatesIn(this.topology.projection)); + + LOGGER.debug("coordinates:"); + LOGGER.debug(coordinatesTable); + + valhallaRequest.coordinates = coordinatesTable; + + // Gestion des contraintes d'exclusion. + if (request.constraints && Array.isArray(request.constraints) && request.constraints.length > 0) { + + valhallaRequest.exclude = []; + for (let i = 0; i < request.constraints.length; i++) { + let constraint = request.constraints[i]; + + if (constraint.type === "banned") { + constraints.push(constraint.field); + } else { + // ce sont des contraintes non gérées donc il n'y a rien à faire + LOGGER.debug("no banned contraints"); + } + + } + + } else { + // il n'y rien à faire car pas de contraintes + LOGGER.debug("no contraints"); + } + + } else { + + // TODO: qu'est-ce qui se passe si on arrive là, doit-on retourner une erreur ou une promesse + LOGGER.error("type of request not found"); + + } + // --- + let locationsString = `"locations":[`; + valhallaRequest.coordinates.forEach(location => { + locationsString += `{"lat": ${location[1]}, "lon": ${location[0]}},`; + }); + locationsString = locationsString.slice(0, -1); + locationsString += "]"; + + const costingString = `"costing":"${this._configuration.cost.compute.configuration.costing}"`; + // Permet de grandement se simplifier le parsing !! + const optionsString = `"directions_options":{"format":"osrm"}`; + const commandString = `valhalla_service ${this._configuration.storage.config} route '{${locationsString},${costingString},${optionsString}}' `; + LOGGER.info(commandString); + + return new Promise( (resolve, reject) => { + + try { + exec(commandString, (err, stdout, stderr) => { + + // Du moment qu'OSRM a répondu, on considère que la source est joignable + this.state = "green"; + + if (err) { + // mais on ne renvoie pas l'erreur à l'utilisateur + reject(errorManager.createError(" No path found ", 404)); + LOGGER.error("valhalla error for route :"); + LOGGER.error(err); + + } else { + + LOGGER.debug("valhalla response for route :"); + LOGGER.debug(stdout); + + try { + resolve(this.writeRouteResponse(request, stdout)); + } catch (error) { + reject(error); + } + + } + + }); + + } catch (error) { + // Pour une raison que l'on ignore, la source n'est plus joignable + this.state = "red"; + LOGGER.error(error); + reject("Internal VALHALLA error"); + } + }); + + } else if (request.operation === "isochrone") { + + LOGGER.debug("operation request is isochrone"); + + if (request.type === "isochroneRequest") { + + LOGGER.debug("type of request is isochroneRequest"); + let constraints = new Array(); + + // Gestion des contraintes d'exclusion. + if (request.constraints && Array.isArray(request.constraints) && request.constraints.length > 0) { + + valhallaRequest.exclude = []; + for (let i = 0; i < request.constraints.length; i++) { + let constraint = request.constraints[i]; + + if (constraint.type === "banned") { + constraints.push(constraint.field); + } else { + // ce sont des contraintes non gérées donc il n'y a rien à faire + LOGGER.debug("no banned contraints"); + } + + } + + } else { + // il n'y rien à faire car pas de contraintes + LOGGER.debug("no contraints"); + } + + // --- + // Conversion en unités valhalla : minutes et kilomètres... + let costValue; + if (request.costType === "time") { + costValue = request.costValue / 60; + } + if (request.costType === "distance") { + costValue = request.costValue / 1000; + } + + let reverse = "false"; + if (request.direction === "arrival") { + reverse = "true"; + } + + const locationsString = `"locations":[{"lat":${request.point.lat},"lon":${request.point.lon}}]`; + const costingString = `"costing":"${this._configuration.cost.compute.configuration.costing}"`; + const contoursString = `"contours":[{"${request.costType}":${costValue}}]`; + const reverseString = `"reverse":${reverse}`; + const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${contoursString},${reverseString}}' `; + LOGGER.info(commandString); + + return new Promise( (resolve, reject) => { + + try { + exec(commandString, (err, stdout, stderr) => { + + // Du moment qu'OSRM a répondu, on considère que la source est joignable + this.state = "green"; + + if (err) { + // mais on ne renvoie pas l'erreur à l'utilisateur + LOGGER.error("valhalla error for route :"); + LOGGER.error(err); + reject(errorManager.createError(" No path found ", 404)); + + } else { + + LOGGER.debug("valhalla response for iso :"); + LOGGER.debug(stdout); + + try { + resolve(this.writeIsochroneResponse(request, stdout)); + } catch (error) { + reject(error); + } + + } + + }); + + } catch (error) { + // Pour une raison que l'on ignore, la source n'est plus joignable + this.state = "red"; + LOGGER.error(error); + reject("Internal VALHALLA error"); + } + }); + + } else { + // TODO: qu'est-ce qui se passe si on arrive là, doit-on retourner une erreur ou une promesse + LOGGER.error("type of request not found"); + } + + } else { + + // TODO: qu'est-ce qui se passe si on arrive là, doit-on retourner une erreur ou une promesse ? + LOGGER.error("request operation not found"); + + } + + } + + /** + * + * @function + * @name writeRouteResponse + * @description Pour traiter la réponse du moteur et la ré-écrire pour le proxy. + * Ce traitement est placé ici car c'est à la source de renvoyer une réponse adaptée au proxy. + * C'est cette fonction qui doit vérifier le contenu de la réponse. Une fois la réponse envoyée + * au proxy, on considère qu'elle est correcte. + * @param {Request} request - Objet Request ou dérivant de la classe Request + * @param {string} valhallaResponseStr - Réponse de valhalla + * + */ + writeRouteResponse (routeRequest, valhallaResponseStr) { + + LOGGER.debug("writeRouteResponse()"); + + let resource; + let start; + let end; + let profile; + let optimization; + let valhallaResponse; + let routes = new Array(); + + // Récupération des paramètres de la requête que l'on veut transmettre dans la réponse + // --- + // resource + resource = routeRequest.resource; + + // profile + profile = routeRequest.profile; + + // optimization + optimization = routeRequest.optimization; + // --- + + // Lecture de la réponse Valhalla + // --- + try { + valhallaResponse = JSON.parse(valhallaResponseStr); + } catch (error) { + LOGGER.error("unable to parse valhalla response") + LOGGER.error(valhallaResponseStr) + LOGGER.error(error) + } + + if (valhallaResponse.waypoints.length < 2) { + // Cela veut dire que l'on n'a pas un start et un end dans la réponse OSRM + throw errorManager.createError(" OSRM response is invalid: the number of waypoints is lower than 2. "); + } else { + LOGGER.debug("osrm response has 2 or more waypoints"); + } + + // projection demandée dans la requête + let askedProjection = routeRequest.start.projection; + LOGGER.debug("asked projection: " + askedProjection); + + LOGGER.debug("topology projection: " + this.topology.projection); + + // start + start = new Point(valhallaResponse.waypoints[0].location[0], valhallaResponse.waypoints[0].location[1], this.topology.projection); + if (!start.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of start in OSRM response. "); + } else { + LOGGER.debug("start in asked projection:"); + LOGGER.debug(start); + } + + // end + end = new Point(valhallaResponse.waypoints[valhallaResponse.waypoints.length-1].location[0], valhallaResponse.waypoints[valhallaResponse.waypoints.length-1].location[1], this.topology.projection); + if (!end.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of end in OSRM response. "); + } else { + LOGGER.debug("end in asked projection:"); + LOGGER.debug(end); + } + + let routeResponse = new RouteResponse(resource, start, end, profile, optimization); + + if (valhallaResponse.routes.length === 0) { + // Cela veut dire que l'on n'a pas un start et un end dans la réponse OSRM + throw errorManager.createError(" No route found ", 404); + } else { + LOGGER.debug("osrm response has 1 or more routes"); + } + + // routes + // Il peut y avoir plusieurs itinéraires + for (let i = 0; i < valhallaResponse.routes.length; i++) { + + LOGGER.debug("osrm route number " + i); + + let portions = new Array(); + let currentOsrmRoute = valhallaResponse.routes[i]; + + // On commence par créer l'itinéraire avec les attributs obligatoires + routes[i] = new Route( new Line(currentOsrmRoute.geometry, "polyline", this._topology.projection, 6) ); + if (!routes[i].geometry.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of geometry in OSRM response. "); + } else { + LOGGER.debug("route geometry is converted"); + } + + // On récupère la distance et la durée + routes[i].distance = new Distance(currentOsrmRoute.distance,"meter"); + routes[i].duration = new Duration(currentOsrmRoute.duration,"second"); + + // On doit avoir une égalité entre ces deux valeurs pour la suite + // Si ce n'est pas le cas, c'est qu'OSRM n'a pas le comportement attendu... + if (currentOsrmRoute.legs.length !== valhallaResponse.waypoints.length-1) { + throw errorManager.createError(" OSRM response is invalid: the number of legs is not proportionnal to the number of waypoints. "); + } else { + LOGGER.debug("number of osrm legs et asked waypoints are compatible"); + } + + // On va gérer les portions qui sont des parties de l'itinéraire entre deux points intermédiaires + for (let j = 0; j < currentOsrmRoute.legs.length; j++) { + + LOGGER.debug("Portion (osrm legs) number " + j + " for route number " + i); + + let currentOsrmRouteLeg = currentOsrmRoute.legs[j]; + + let legStart = new Point(valhallaResponse.waypoints[j].location[0], valhallaResponse.waypoints[j].location[1], this.topology.projection); + if (!legStart.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of leg start in OSRM response. "); + } else { + LOGGER.debug("portion start in asked projection:"); + LOGGER.debug(legStart); + } + + let legEnd = new Point(valhallaResponse.waypoints[j+1].location[0], valhallaResponse.waypoints[j+1].location[1], this.topology.projection); + if (!legEnd.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of leg end in OSRM response. "); + } else { + LOGGER.debug("portion end in asked projection:"); + LOGGER.debug(legEnd); + } + + portions[j] = new Portion(legStart, legEnd); + + // On récupère la distance et la durée + portions[j].distance = new Distance(currentOsrmRouteLeg.distance,"meter"); + portions[j].duration = new Duration(currentOsrmRouteLeg.duration,"second"); + + // Steps + let steps = new Array(); + + // On va associer les étapes à la portion concernée + for (let k=0; k < currentOsrmRouteLeg.steps.length; k++) { + + LOGGER.debug("Step number " + k + " of portion number " + j + " for route number " + i); + + let currentOsrmRouteStep = currentOsrmRouteLeg.steps[k]; + steps[k] = new Step( new Line(currentOsrmRouteStep.geometry, "polyline", this._topology.projection, 6) ); + if (!steps[k].geometry.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of step's geometry in OSRM response. "); + } else { + LOGGER.debug("step geometry is converted"); + } + + // Ajout de l'attribut name + let nameAttributs; + try { + nameAttributs = JSON.parse(currentOsrmRouteStep.name); + } catch(error) { + // Ce doit être une chaîne de caractère + nameAttributs = currentOsrmRouteStep.name; + } + steps[k].setAttributById("name", nameAttributs); + + // On récupère la distance et la durée + steps[k].distance = new Distance(currentOsrmRouteStep.distance,"meter"); + steps[k].duration = new Duration(currentOsrmRouteStep.duration,"second"); + + steps[k].instruction = {}; + steps[k].instruction.type = currentOsrmRouteStep.maneuver.type; + if (currentOsrmRouteStep.maneuver.modifier) { + steps[k].instruction.modifier = currentOsrmRouteStep.maneuver.modifier; + } + if (currentOsrmRouteStep.maneuver.exit) { + steps[k].instruction.exit = currentOsrmRouteStep.maneuver.exit; + } + } + + portions[j].steps = steps; + + } + + routes[i].portions = portions; + + } + + routeResponse.routes = routes; + + return routeResponse; + } + + /** + * + * @function + * @name writeIsochroneResponse + * @description Pour traiter la réponse du moteur et la ré-écrire pour le proxy. + * Ce traitement est placé ici car c'est à la source de renvoyer une réponse adaptée au proxy. + * C'est cette fonction qui doit vérifier le contenu de la réponse. Une fois la réponse envoyée + * au proxy, on considère qu'elle est correcte. + * @param {Request} request - Objet Request ou dérivant de la classe Request + * @param {string} valhallaResponseStr - Réponse de valhalla + * + */ + writeIsochroneResponse(isochroneRequest, valhallaResponseStr) { + let point = {}; + let geometry = {}; + let valhallaResponse; + + // Lecture de la réponse Valhalla + // --- + try { + valhallaResponse = JSON.parse(valhallaResponseStr); + } catch (error) { + LOGGER.error("unable to parse valhalla response") + LOGGER.error(valhallaResponseStr) + LOGGER.error(error) + } + + // Si pgrResponse est vide + if (valhallaResponse.features.length === 0) { + throw errorManager.createError(" No data found ", 404); + } + + // Création d'un objet Point (utile plus tard). + point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, this.topology.projection); + + let rawGeometry = valhallaResponse.features[0].geometry; + + // Cas où il n'y a pas d'isochrone car costValue trop faible + if (rawGeometry === null) { + rawGeometry = { + type: 'Point', + coordinates: [ + isochroneRequest.point.lon, + isochroneRequest.point.lat + ] + }; + } + + // Création d'un objet Polygon à partir du GeoJSON reçu. + geometry = new Polygon(rawGeometry, "geojson", this._topology.projection); + + /* Envoi de la réponse au proxy. */ + return new IsochroneResponse( + point, + isochroneRequest.resource, + isochroneRequest.costType, + isochroneRequest.costValue, + geometry, + isochroneRequest.profile, + isochroneRequest.direction, + isochroneRequest.askedProjection, + isochroneRequest.timeUnit, + isochroneRequest.distanceUnit + ); + } +} diff --git a/src/js/utils/storageManager.js b/src/js/utils/storageManager.js index 0732f62..1c533e9 100644 --- a/src/js/utils/storageManager.js +++ b/src/js/utils/storageManager.js @@ -37,7 +37,34 @@ module.exports = { LOGGER.error("Le fichier " + jsonStorage.file + " n'existe pas."); return false; } - + } else if (jsonStorage.tar || jsonStorage.config) { + // Vérification que le fichier existe et peut être lu. + if (fs.existsSync(jsonStorage.tar)) { + try { + fs.accessSync(jsonStorage.tar, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier " + jsonStorage.tar + " ne peut etre lu."); + return false; + } + storageFound = true; + } else { + LOGGER.error("Le fichier " + jsonStorage.tar + " n'existe pas."); + return false; + } + } else if (jsonStorage.config) { + // Vérification que le fichier existe et peut être lu. + if (fs.existsSync(jsonStorage.config)) { + try { + fs.accessSync(jsonStorage.config, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier " + jsonStorage.config + " ne peut etre lu."); + return false; + } + storageFound = true; + } else { + LOGGER.error("Le fichier " + jsonStorage.config + " n'existe pas."); + return false; + } } else if (jsonStorage.costColumn) { // TODO: Pas trop possible de vérifier sans requête storageFound = true; diff --git a/test/functional/request/cucumber/configurations/local-service.json b/test/functional/request/cucumber/configurations/local-service.json index 9ebb29b..2bbd40c 100644 --- a/test/functional/request/cucumber/configurations/local-service.json +++ b/test/functional/request/cucumber/configurations/local-service.json @@ -51,9 +51,24 @@ "resource": "bduni-idf-osrm", "coordinates": "2.333865,48.881989" } + }, + { + "id": "isochrone-valhalla", + "parameters": { + "resource": "bduni-idf-valhalla", + "point": "2.333865,48.881989" + } + }, + { + "id": "route-valhalla", + "parameters": { + "resource": "bduni-idf-valhalla", + "start": "2.333865,48.881989", + "end": "2.344851,48.872393" + } } ], "alternativeParameters" : { "url" : "127.0.0.1" } -} \ No newline at end of file +} diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index c2c06a3..490f667 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -7,7 +7,7 @@ Feature: Road2-PGR Scenario Outline: [] Route sur l'API simple 1.0.0 Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -15,61 +15,61 @@ Feature: Road2-PGR Examples: | method | | GET | - | POST | + | POST | - Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road And the response should contain an attribute "constraints.[1].key" - Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes dont une invalide sur une ressource pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes dont une invalide sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" - Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes sur une ressource pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road And the response should contain an attribute "constraints.[1].key" - Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont une invalide sur une ressource pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont une invalide sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" - Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont deux invalides sur une ressource pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec trois contraintes dont deux invalides sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"test1"}\|{"constraintType":"test2","key":"waytype","operator":"=","value":"tunnel"}\|{"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -81,7 +81,7 @@ Feature: Road2-PGR | value | | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -94,7 +94,7 @@ Feature: Road2-PGR | value | | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -107,7 +107,7 @@ Feature: Road2-PGR | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -121,7 +121,7 @@ Feature: Road2-PGR | {"constraintType":"banned","key":"waytype","operator":"=","value":"test"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"tunnel"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -134,53 +134,53 @@ Feature: Road2-PGR | {"constraintType":"banned","key":"waytype","operator":"=","value":"test1"} | | {"constraintType":"test2","key":"waytype","operator":"=","value":"tunnel"} | | {"constraintType":"banned","key":"waytype","operator":"=","value":"pont"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" - Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"importance","operator":">=","value":4} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road And the response should contain an attribute "constraints.[0].key" - Scenario: [GET] Route sur l'API simple 1.0.0 avec une mauvaise contrainte spécifique pgr + Scenario: [GET] Route sur l'API simple 1.0.0 avec une mauvaise contrainte spécifique pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"importance","operator":">=","value":"4"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" - Scenario: [POST] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr + Scenario: [POST] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr Given an "POST" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with table parameters of object for "constraints": | value | | {"constraintType":"banned","key":"importance","operator":">=","value":4} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road And the response should contain an attribute "constraints.[0].key" - Scenario: [POST] Route sur l'API simple 1.0.0 avec une mauvaise contrainte spécifique pgr + Scenario: [POST] Route sur l'API simple 1.0.0 avec une mauvaise contrainte spécifique pgr Given an "POST" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" And with table parameters of object for "constraints": | value | | {"constraintType":"banned","key":"importance","operator":">=","value":"4"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -191,7 +191,7 @@ Feature: Road2-PGR And with query parameters: | key | value | | constraints | {"constraintType":"prefer","key":"cpx_classement_administratif","operator":"=","value":"autoroute"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -203,7 +203,7 @@ Feature: Road2-PGR And with query parameters: | key | value | | constraints | {"constraintType":"prefer","key":"cpx_classement_administratif","operator":"=","value":"test"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -214,7 +214,7 @@ Feature: Road2-PGR And with table parameters of object for "constraints": | value | | {"constraintType":"prefer","key":"cpx_classement_administratif","operator":"=","value":"autoroute"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -226,7 +226,7 @@ Feature: Road2-PGR And with table parameters of object for "constraints": | value | | {"constraintType":"prefer","key":"cpx_classement_administratif","operator":"=","value":"test"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -237,7 +237,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"avoid","key":"cpx_classement_administratif","operator":"=","value":"autoroute"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -249,7 +249,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"avoid","key":"cpx_classement_administratif","operator":"=","value":"test"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -260,7 +260,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"avoid","key":"cpx_classement_administratif","operator":"=","value":"autoroute"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -272,7 +272,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"avoid","key":"cpx_classement_administratif","operator":"=","value":"test"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -283,7 +283,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"importance","operator":"<=","value":1} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -295,7 +295,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"banned","key":"importance","operator":"<=","value":1} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -307,7 +307,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"importance","operator":"=","value":1} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -319,7 +319,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"banned","key":"importance","operator":"=","value":1} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -331,7 +331,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"prefer","key":"importance","operator":">=","value":5} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -343,7 +343,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"prefer","key":"importance","operator":">=","value":5} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -355,7 +355,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"prefer","key":"importance","operator":">","value":5} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -367,7 +367,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"prefer","key":"importance","operator":">","value":5} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -379,7 +379,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"prefer","key":"importance","operator":"!=","value":6} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -391,7 +391,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"prefer","key":"importance","operator":"!=","value":6} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -403,7 +403,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"avoid","key":"importance","operator":"<","value":2} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -415,7 +415,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"avoid","key":"importance","operator":"<","value":2} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid road @@ -427,7 +427,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"avoid","key":"importance","operator":"<","value":"2"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -438,7 +438,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"avoid","key":"importance","operator":"<","value":"2"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -449,7 +449,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with query parameters: | key | value | | constraints | {"constraintType":"avoid","key":"importance","operator":"<","value":"test"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -460,7 +460,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And with table parameters of object for "constraints": | value | | {"constraintType":"avoid","key":"importance","operator":"<","value":"test"} | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" @@ -468,7 +468,7 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid iso @@ -476,15 +476,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans ressource + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans ressource Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And without query parameters: - | key | + | key | | resource | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'resource' not found" @@ -492,15 +492,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec une mauvaise ressource + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec une mauvaise ressource Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And with query parameters: | key | value | | resource | bduni-idf-osrm-2 | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'resource' is invalid" @@ -508,15 +508,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans point + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans point Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And without query parameters: - | key | + | key | | point | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'point' not found" @@ -524,15 +524,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais point + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais point Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And with query parameters: | key | value | | point | -9,-410 | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'point' is invalid" @@ -540,15 +540,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans costValue + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans costValue Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And without query parameters: - | key | + | key | | costValue | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' not found" @@ -556,7 +556,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais costValue (1) Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -564,7 +564,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costValue | -9,-410 | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' is invalid" @@ -572,7 +572,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais costValue (2) Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -580,7 +580,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costValue | Infinity | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' is invalid" @@ -588,7 +588,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais costValue (3) Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -596,7 +596,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costValue | NaN | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' is invalid" @@ -604,7 +604,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais costValue (4) Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -612,7 +612,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costValue | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' is invalid" @@ -620,7 +620,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un costValue trop petit Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -628,7 +628,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costValue | 5 | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' is invalid" @@ -636,7 +636,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un costValue trop grand Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -644,7 +644,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costValue | 30000 | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costValue' is invalid" @@ -652,15 +652,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans costType + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sans costType Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And without query parameters: - | key | + | key | | costType | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costType' not found" @@ -668,7 +668,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais costType Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -676,7 +676,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | costType | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'costType' is invalid" @@ -684,7 +684,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais profile @@ -693,7 +693,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | profile | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'profile' is invalid" @@ -701,7 +701,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais direction @@ -710,7 +710,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | direction | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'direction' is invalid" @@ -718,7 +718,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais geometryFormat Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -726,7 +726,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | geometryFormat | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'geometryFormat' is invalid" @@ -734,7 +734,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un geometryFormat polyline Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -742,14 +742,14 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | geometryFormat | polyline | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais distanceUnit Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -758,7 +758,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 | key | value | | distanceUnit | test | | costType | distance | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'distanceUnit' is invalid" @@ -766,7 +766,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais timeUnit Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -774,7 +774,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | timeUnit | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'timeUnit' is invalid" @@ -782,7 +782,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais crs Given an "" request on operation "isochrone" in api "simple" "1.0.0" @@ -790,7 +790,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with query parameters: | key | value | | crs | test | - When I send the request + When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" And the response should contain an attribute "error.message" with value "Parameter 'crs' is invalid" @@ -798,15 +798,15 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Examples: | method | | GET | - | POST | + | POST | - Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource pgr + Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource pgr Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" And with query parameters: | key | value | | constraints | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid iso @@ -818,7 +818,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with table parameters of object for "constraints": | value | | {"constraintType":"banned","key":"waytype","operator":"=","value":"autoroute"} | - When I send the request + When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" And the response should contain a complete and valid iso diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature new file mode 100644 index 0000000..562fced --- /dev/null +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature @@ -0,0 +1,52 @@ +Feature: Road2-Valhalla + Tests fonctionnels complémentaires de Road2 via Valhalla + + Background: + Given I have loaded all my test configuration in "../../configurations/local-service.json" + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource valhalla + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-valhalla" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 31000 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + + Examples: + | method | + | GET | + | POST | + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource valhalla + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-valhalla" + And with query parameters: + | key | value | + | costType | distance | + | costValue | 1000 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + + Examples: + | method | + | GET | + | POST | + +Scenario Outline: [] Route sur l'API simple 1.0.0 avec valhalla + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-valhalla" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + + Examples: + | method | + | GET | + | POST | diff --git a/test/integration/mocha/resources/integrationValhallaResource.js b/test/integration/mocha/resources/integrationValhallaResource.js new file mode 100644 index 0000000..56a2267 --- /dev/null +++ b/test/integration/mocha/resources/integrationValhallaResource.js @@ -0,0 +1,104 @@ +const assert = require('assert'); +const ValhallaResource = require('../../../../src/js/resources/valhallaResource'); +const RouteRequest = require('../../../../src/js/requests/routeRequest'); +const logManager = require('../../../unit/mocha/logManager'); + +const sinon = require('sinon'); + +describe('Test de la classe ValhallaResource', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let resourceConfiguration = { + "resource": { + "id": "test-valhalla", + "type": "valhalla", + "description": "Exemple d'une ressource Valhalla", + "topology": { + "description": "Données OSM", + "storage": { + "file": "/home/docker/data/corse-latest.osm.pbf" + }, + "projection": "EPSG:4326" + }, + "sources": [ + { + "id": "corse-auto-valhalla", + "type": "valhalla", + "storage": { + "tar": "/home/docker/data/corse-latest-valhalla-tiles.tar", + "dir": "/home/docker/data/corse-latest-valhalla-tiles/", + "config": "/home/docker/data/valhalla.json" + }, + "cost": { + "profile": "car", + "optimization": "fastest", + "compute": { + "storage": { + "file": "/home/docker/config/graph_bdtopo.lua" + }, + "configuration": { + "costing": "auto", + "storage": { + "file": "/home/docker/config/costs_calculation.json" + } + } + } + } + } + ], + "availableOperations":[ + + ], + "defaultSourceId": "corse-auto-valhalla", + "boundingBox": "-90,-180,90,180", + "defaultProjection": "EPSG:4326", + "availableProjections": ["EPSG:4326","EPSG:2154"] + } + }; + + let resource = new ValhallaResource(resourceConfiguration); + + describe('Test du constructeur et des getters', function() { + + it('Get Id', function() { + assert.equal(resource.id, "corse-osm"); + }); + + it('Get Type', function() { + assert.equal(resource.type, "osrm"); + }); + + it('Get Configuration', function() { + assert.deepEqual(resource.configuration, resourceConfiguration.resource); + }); + + it('Get waysAttributes', function() { + assert.deepEqual(resource.waysAttributes, ["name"]); + }); + + it('Get LinkedSource', function() { + let reference = {}; + reference["carfastest"] = "corse-auto-valhalla"; + assert.deepEqual(resource.linkedSource, reference); + }); + + }); + + describe('Test de getSourceIdFromRequest', function() { + + // Pour ne pas dépendre de la classe RouteRequest + let request = sinon.mock(RouteRequest); + request.profile = "car"; + request.optimization = "fastest"; + + it('getSourceIdFromRequest()', function() { + assert.equal(resource.getSourceIdFromRequest(request), "corse-auto-valhalla"); + }); + + }); + +}); diff --git a/test/integration/mocha/sources/integrationValhallaSource.js b/test/integration/mocha/sources/integrationValhallaSource.js new file mode 100644 index 0000000..39d0d6a --- /dev/null +++ b/test/integration/mocha/sources/integrationValhallaSource.js @@ -0,0 +1,135 @@ +const assert = require('assert'); +const valhallaSource = require('../../../../src/js/sources/valhallaSource'); +const RouteRequest = require('../../../../src/js/requests/routeRequest'); +const logManager = require('../logManager'); +const OSRM = require("osrm"); + +const sinon = require('sinon'); +const mockfs = require('mock-fs'); + +describe('Test de la classe valhallaSource', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + + mockfs({ + "/home/docker/data": { + "corse-latest.osm.pbf": "", + "corse-latest-valhalla-tiles.tar": "", + }, + "/home/docker/config": { + "graph_bdtopo.lua": "", + "costs_calculation.json": "", + }, + "/home/docker/data/corse-latest-valhalla-tiles": { + } + }); + }); + + after(() => { + mockfs.restore(); + }); + + let sourceDescription = { + "id": "corse-auto-valhalla", + "type": "valhalla", + "storage": { + "tar": "/home/docker/data/corse-latest-valhalla-tiles.tar", + "dir": "/home/docker/data/corse-latest-valhalla-tiles/", + "config": "/home/docker/data/valhalla.json" + }, + "cost": { + "profile": "car", + "optimization": "fastest", + "compute": { + "storage": { + "file": "/home/docker/config/graph_bdtopo.lua" + }, + "configuration": { + "costing": "auto", + "storage": { + "file": "/home/docker/config/costs_calculation.json" + } + } + } + } + }; + + let topology = { + "id": "corse-osm", + "type": "osm", + "description": "Données OSM sur la Corse.", + "storage": { + "file": "/home/docker/data/corse-latest.osm.pbf" + }, + "projection": "EPSG:4326", + "bbox": "-90,-180,90,180", + + }; + + let source = new valhallaSource(sourceDescription, topology); + + describe('Test du constructeur et des getters', function() { + + it('Get Source id', function() { + assert.equal(source.id, "corse-car-fastest"); + }); + + it('Get Source type', function() { + assert.equal(source.type, "osrm"); + }); + + it('Get Source connected', function() { + assert.equal(source.connected, false); + }); + + it('Get Source configuration', function() { + assert.deepEqual(source.configuration, sourceDescription); + }); + + }); + + // describe('Test de connect()', function() { + + // // Impossible à tester sans les fichiers : connect() appelle le constructeur OSRM qui a besoin + // // d'un fichier, et qui visiblement le check (cf. mockfs dans le script before() ) + // it('Connect()', async function() { + // const sourceConnected = await source.connect(); + // assert.equal(sourceConnected, true); + // }); + + // }); + + describe('Test de disconnect()', function() { + + it('Disconnect()', async function() { + await source.disconnect(); + }); + + }); + + describe('Test de computeRequest() et writeRouteResponse()', function() { + + let resource = "resource-test"; + let start = {lon: 8.732901, lat: 41.928821, getCoordinatesIn(toto) { return [8.732901, 41.928821];} }; + let end = {lon: 8.76385, lat: 41.953932, getCoordinatesIn(toto) { return [8.76385, 41.953932];}}; + let profile = "car-test"; + let optimization = "fastest-test"; + let routeRequest = new RouteRequest(resource, start, end, profile, optimization); + + // TODO: better fake osrm response + const fakeOsrmResponse = {"routes":[{"geometry":"cf|~Fssht@tAgLFiNqJTaEuFiEpEgIxCsLdDwDw@oIac@mAg@cKkTBiBeAaByCqSkHc[mGaQHiGgB_@wL{[_FvBsDmEuEeB{RjGk@e@Z{B","legs":[{"summary":"Avenue Colonel Colonna d'Ornano, Route de Mezzavia","weight":449.7,"duration":449.7,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[204],"location":[8.732901,41.928821]}],"driving_side":"right","geometry":"cf|~Fssht@B@","mode":"driving","maneuver":{"bearing_after":204,"bearing_before":0,"location":[8.732901,41.928821],"type":"depart"},"weight":5.3,"duration":5.3,"name":"Avenue du Maréchal Moncey","distance":2.4},{"intersections":[{"out":1,"in":0,"entry":[false,true,true],"bearings":[15,120,300],"location":[8.732889,41.928801]},{"out":0,"in":1,"entry":[true,false,true],"bearings":[105,285,315],"location":[8.734336,41.92853]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[105,135,285],"location":[8.734622,41.92846]}],"driving_side":"right","geometry":"_f|~Fqsht@Rw@Hq@HwBL_ALw@LoAIuCG{BA]AY@SZkC","mode":"driving","maneuver":{"bearing_after":113,"bearing_before":199,"location":[8.732889,41.928801],"modifier":"left","type":"turn"},"ref":"D 11","weight":53.3,"duration":53.3,"name":"Avenue Colonel Colonna d'Ornano","distance":388.2},{"intersections":[{"out":0,"in":3,"entry":[true,false,true,false],"bearings":[15,105,180,285],"location":[8.737468,41.928353]},{"out":3,"in":1,"entry":[true,false,true,true],"bearings":[105,165,285,345],"location":[8.737421,41.928889]},{"out":0,"in":1,"entry":[true,false,false],"bearings":[15,180,345],"location":[8.737359,41.930203]}],"driving_side":"right","geometry":"ec|~Fepit@MC_@Gg@LUFm@PwEEe@Qc@Y","mode":"driving","maneuver":{"bearing_after":9,"bearing_before":104,"location":[8.737468,41.928353],"modifier":"left","type":"turn"},"weight":22.8,"duration":22.8,"name":"Boulevard Dominique Paoli","distance":253.1},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[60,210,270],"location":[8.737582,41.930566]},{"out":0,"in":1,"entry":[true,false,true],"bearings":[60,240,315],"location":[8.737632,41.930591]},{"out":0,"in":2,"entry":[true,false,false],"bearings":[45,105,240],"location":[8.737712,41.93063]},{"out":0,"in":1,"entry":[true,false,true],"bearings":[45,225,330],"location":[8.737925,41.930761]}],"driving_side":"right","geometry":"aq|~F{pit@CIGOYk@g@aAi@aA","mode":"driving","maneuver":{"bearing_after":54,"bearing_before":28,"location":[8.737582,41.930566],"modifier":"slight right","type":"new name"},"weight":14.5,"duration":14.5,"name":"Avenue du Président Kennedy","distance":107.6},{"intersections":[{"out":2,"in":1,"entry":[true,false,true],"bearings":[120,225,330],"location":[8.738593,41.931173]},{"out":2,"in":1,"entry":[true,false,true],"bearings":[45,150,315],"location":[8.738491,41.931273]},{"out":2,"in":1,"entry":[true,false,true],"bearings":[45,135,315],"location":[8.738055,41.931646]},{"out":2,"in":1,"entry":[true,false,true],"bearings":[75,165,345],"location":[8.737414,41.932566]},{"out":0,"in":2,"entry":[true,true,false,true],"bearings":[15,60,165,300],"location":[8.737348,41.932831]},{"out":2,"in":1,"entry":[true,false,true],"bearings":[30,195,345],"location":[8.737389,41.932923]}],"driving_side":"right","geometry":"yt|~Fewit@SRkAtAa@b@gAbAMF_APs@JQGI@aA\\o@d@Y^]Tc@HuBJ","mode":"driving","maneuver":{"bearing_after":322,"bearing_before":49,"location":[8.738593,41.931173],"modifier":"left","type":"end of road"},"weight":28.2,"duration":28.2,"name":"Montée Saint-Jean","distance":426.4},{"intersections":[{"out":2,"in":1,"entry":[false,false,true],"bearings":[135,180,345],"location":[8.736664,41.934585]},{"out":2,"in":1,"entry":[true,false,true],"bearings":[75,165,330],"location":[8.736303,41.935359]}],"driving_side":"right","geometry":"ej}~Fckit@e@PKDIB}Aj@_CfAu@Fu@@e@Q","mode":"driving","maneuver":{"bearing_after":337,"bearing_before":355,"location":[8.736664,41.934585],"modifier":"straight","type":"new name"},"weight":17.5,"duration":17.5,"name":"Route d'Alata","distance":251.1},{"intersections":[{"out":0,"in":1,"entry":[true,false,false],"bearings":[90,195,285],"location":[8.735976,41.936731]}],"driving_side":"right","geometry":"qw}~F{fit@?KAKCIEGGEGAI@","mode":"driving","maneuver":{"exit":1,"bearing_after":95,"bearing_before":16,"location":[8.735976,41.936731],"modifier":"right","type":"roundabout"},"weight":3.3,"duration":3.3,"name":"Boulevard de l'Abbé Recco","distance":34.7},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[75,180,330],"location":[8.736216,41.936918]}],"driving_side":"right","geometry":"wx}~Fkhit@e@aDm@kDs@uDc@_COq@_BgGm@cBOe@","mode":"driving","maneuver":{"exit":1,"bearing_after":71,"bearing_before":351,"location":[8.736216,41.936918],"modifier":"right","type":"exit roundabout"},"weight":28.1,"duration":28.1,"name":"Boulevard de l'Abbé Recco","distance":494.6},{"intersections":[{"out":0,"in":1,"entry":[true,false,false],"bearings":[135,240,330],"location":[8.7417,41.938654]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[60,150,255],"location":[8.74199,41.938595]}],"driving_side":"right","geometry":"qc~~Fsjjt@JS@SCQCKGEMCK?OH","mode":"driving","maneuver":{"exit":2,"bearing_after":130,"bearing_before":57,"location":[8.7417,41.938654],"modifier":"right","type":"roundabout"},"weight":3.8,"duration":3.8,"name":"","distance":61.3},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[45,150,300],"location":[8.742045,41.938866]}],"driving_side":"right","geometry":"}d~~Fyljt@W[[u@uAiCm@gAcDcH[o@Uq@M]Mk@","mode":"driving","maneuver":{"exit":2,"bearing_after":39,"bearing_before":336,"location":[8.742045,41.938866],"modifier":"right","type":"exit roundabout"},"weight":23,"duration":23,"name":"","distance":394.8},{"intersections":[{"out":0,"in":1,"entry":[true,false,false],"bearings":[150,240,345],"location":[8.745834,41.941003]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[75,165,285],"location":[8.746049,41.940898]}],"driving_side":"right","geometry":"gr~~Fmdkt@HKFM@QAQGMIIKEMB","mode":"driving","maneuver":{"exit":2,"bearing_after":146,"bearing_before":66,"location":[8.745834,41.941003],"modifier":"right","type":"roundabout"},"weight":3.9000000000000004,"duration":3.9000000000000004,"name":"","distance":59.2},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[60,180,330],"location":[8.746274,41.941128]},{"out":0,"in":1,"entry":[true,false,true],"bearings":[75,240,285],"location":[8.746626,41.94126]},{"out":1,"in":2,"entry":[false,true,false],"bearings":[30,75,255],"location":[8.747412,41.941483]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[70,82,250],"location":[8.747665,41.941553]},{"out":0,"in":2,"entry":[true,false,false,true],"bearings":[75,240,255,270],"location":[8.749168,41.94197]}],"driving_side":"right","geometry":"as~~Fegkt@YgAG[WsAKk@Ms@_@qBs@yDG]SgA","mode":"driving","maneuver":{"exit":2,"bearing_after":61,"bearing_before":351,"location":[8.746274,41.941128],"modifier":"right","type":"exit roundabout"},"weight":17.6,"duration":17.6,"name":"","distance":302.1},{"intersections":[{"out":1,"in":2,"entry":[false,true,false],"bearings":[0,150,255],"location":[8.749676,41.942111]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[75,180,285],"location":[8.749846,41.942021]},{"out":0,"in":2,"entry":[true,false,false],"bearings":[15,180,225],"location":[8.75,41.942081]}],"driving_side":"right","geometry":"ey~~Fo|kt@JIDWAMIOSE","mode":"driving","maneuver":{"exit":2,"bearing_after":146,"bearing_before":68,"location":[8.749676,41.942111],"modifier":"right","type":"roundabout"},"weight":3.1,"duration":3.1,"name":"Boulevard Louis Campi","distance":44.2},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[75,195,330],"location":[8.750029,41.94218]}],"driving_side":"right","geometry":"sy~~Fu~kt@_AaGYkA_C}I_@iAW_A","mode":"driving","maneuver":{"exit":2,"bearing_after":70,"bearing_before":11,"location":[8.750029,41.94218],"modifier":"right","type":"exit roundabout"},"weight":21.4,"duration":21.4,"name":"Boulevard Louis Campi","distance":373.6},{"intersections":[{"out":0,"in":1,"entry":[true,false,false],"bearings":[135,240,345],"location":[8.754141,41.94355]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[75,135,255],"location":[8.754363,41.943511]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[30,90,210],"location":[8.754453,41.943572]}],"driving_side":"right","geometry":"eb__Gkxlt@FMBOCMCKGECCO@","mode":"driving","maneuver":{"exit":1,"bearing_after":135,"bearing_before":61,"location":[8.754141,41.94355],"modifier":"right","type":"roundabout"},"weight":2.6999999999999997,"duration":2.6999999999999997,"name":"Rue Louis Campi","distance":43.2},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[60,180,330],"location":[8.754461,41.943674]}],"driving_side":"right","geometry":"}b__Gkzlt@oC}Hg@iBy@qCG_AEm@C}@","mode":"driving","maneuver":{"exit":1,"bearing_after":57,"bearing_before":357,"location":[8.754461,41.943674],"modifier":"right","type":"exit roundabout"},"weight":19.9,"duration":19.9,"name":"Rue Louis Campi","distance":343.5},{"intersections":[{"out":1,"in":2,"entry":[false,true,false],"bearings":[0,165,270],"location":[8.758172,41.944968]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[120,195,315],"location":[8.758272,41.944843]},{"out":0,"in":2,"entry":[true,false,false],"bearings":[90,195,285],"location":[8.758437,41.944791]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[30,75,225],"location":[8.758702,41.944872]},{"out":0,"in":2,"entry":[true,false,false],"bearings":[0,105,195],"location":[8.75877,41.94501]}],"driving_side":"right","geometry":"ak__Gqqmt@LGJKDOBQ?IAKEQGKMIMCK@KDIHEL","mode":"driving","maneuver":{"exit":3,"bearing_after":160,"bearing_before":85,"location":[8.758172,41.944968],"modifier":"right","type":"roundabout"},"ref":"T 22","weight":8.8,"duration":8.8,"name":"Route de Mezzavia","distance":100.3},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[45,135,285],"location":[8.758609,41.945214]}],"driving_side":"right","geometry":"ql__Gitmt@[e@Uu@s@_D{@oCiB_CkBmE{@iCM_@Q}@","mode":"driving","maneuver":{"exit":3,"bearing_after":45,"bearing_before":306,"location":[8.758609,41.945214],"modifier":"right","type":"exit roundabout"},"ref":"T 22","weight":49.4,"duration":49.4,"name":"Route de Mezzavia","distance":480.3},{"intersections":[{"out":2,"in":1,"entry":[true,false,true],"bearings":[60,255,330],"location":[8.763419,41.947548]}],"driving_side":"right","geometry":"e{__Gkrnt@u@h@g@\\k@\\m@Pg@?aAe@i@cAgAcB{Ac@yBaA","mode":"driving","maneuver":{"bearing_after":330,"bearing_before":67,"location":[8.763419,41.947548],"modifier":"left","type":"turn"},"weight":57.4,"duration":57.4,"name":"","distance":397.2},{"intersections":[{"out":2,"in":1,"entry":[true,false,true],"bearings":[45,195,345],"location":[8.764357,41.950639]},{"out":2,"in":1,"entry":[true,false,true],"bearings":[60,150,345],"location":[8.763892,41.951919]}],"driving_side":"right","geometry":"on`_Ggxnt@iBXyA`@{@`@c@RkAn@s@VaCf@uAh@","mode":"driving","maneuver":{"bearing_after":348,"bearing_before":21,"location":[8.764357,41.950639],"modifier":"slight left","type":"fork"},"weight":53.5,"duration":53.5,"name":"","distance":372},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[15,165,315],"location":[8.763022,41.953818]}],"driving_side":"right","geometry":"kba_G{ont@QIQIGQA]DUH[Lk@","mode":"driving","maneuver":{"bearing_after":18,"bearing_before":338,"location":[8.763022,41.953818],"modifier":"slight right","type":"fork"},"weight":12.2,"duration":12.2,"name":"","distance":85.1},{"intersections":[{"in":0,"entry":[true],"bearings":[294],"location":[8.763831,41.953897]}],"driving_side":"right","geometry":"{ba_G}tnt@","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":114,"location":[8.763831,41.953897],"type":"arrive"},"weight":0,"duration":0,"name":"","distance":0}],"distance":5015}],"weight_name":"routability","weight":449.7,"duration":449.7,"distance":5015}],"waypoints":[{"hint":"dQ0AgO8zAIAPAAAABAAAAIIAAAAAAAAAoawtQWjKG0D0ELRCAAAAAA8AAAAEAAAAggAAAAAAAAAMAAAA5UCFAHXIfwLlQIUAdch_AgMAvwy-YJX8","name":"Avenue du Maréchal Moncey","location":[8.732901,41.928821]},{"hint":"8iwAgCEtAIAAAAAAFwAAAHoAAAAiAAAApgWzPh9jf0E3v6lCdn-wQQAAAAAXAAAAegAAACIAAAAMAAAAt7mFAGkqgALKuYUAjCqAAgcA_wa-YJX8","name":"","location":[8.763831,41.953897]}]}; + + const fakeClient = sinon.mock(OSRM); + fakeClient.route = sinon.stub().callsArgOnWith(1, source, null, fakeOsrmResponse); + source._osrm = fakeClient; + + it('computeRequest() should return a routeResponse', async function() { + const routeResponse = await source.computeRequest(routeRequest); + assert.equal(routeResponse.resource, "resource-test"); + }); + + }); + +}); From d2d6c557a572c665cef9de07ce3aa769ac4405de Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 21 Oct 2022 15:44:33 +0200 Subject: [PATCH 09/93] maj du changelog.md --- changelog.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/changelog.md b/changelog.md index 49dcf98..7659939 100644 --- a/changelog.md +++ b/changelog.md @@ -3,11 +3,27 @@ ADDED: - La classe Administrator permet de gérer le service via une API. Notamment la création, la suppression et la modification d'un service seront possible. - Cette classe est configurée par un nouveau fichier de configuration. + - Ajout du moteur Valhalla pour les itinéraires et les isochrones CHANGED: - L'option --configCheck au démarrage de Road2 n'a plus exactement le même comportement. - Le fichier server.json permet maintenant de configurer l'administrateur et donc n'a plus le même contenu. Ce dernier est dans service.json. +# 1.1.2 + +FIXED: + - Géométries reprojetées pour les requêtes sur PGRouting + +# 1.1.1 + +FIXED: + - Géométrie étrange quand on est dans une raquette + +# 1.1.0 + +ADDED: + - Ajout de la fonctionnalité nearest via OSRM + # 1.O.14 FIXED: - Mauvaise ligne pour un log From ad99de3f7965b14b200cd85528d85f71c7d316db Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 24 Oct 2022 11:44:25 +0200 Subject: [PATCH 10/93] fix: commande du docker-compose de road2 --- docker/dev/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index d4756d8..b0edc31 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -11,7 +11,7 @@ services: - pgrouting environment: - NODE_ENV=debug - command : "sleep 60000" + command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" ports: - 8080:8080 - 9229:9229 From a63f430fc630fec96fa20972ee5150cd4d38d3ed Mon Sep 17 00:00:00 2001 From: Loic Date: Wed, 26 Oct 2022 11:33:08 +0200 Subject: [PATCH 11/93] =?UTF-8?q?WIP:=20s=C3=A9paration=20des=20sources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/config/service.json | 5 + docker/dev/docker-compose.yml | 2 +- src/js/geography/projectionManager.js | 63 ++++- src/js/resources/resourceManager.js | 21 +- src/js/service/service.js | 89 ++++++- src/js/sources/sourceManager.js | 349 ++++++++++++++++---------- src/js/topology/topologyManager.js | 2 +- 7 files changed, 380 insertions(+), 151 deletions(-) diff --git a/docker/config/service.json b/docker/config/service.json index b9c0620..265db84 100644 --- a/docker/config/service.json +++ b/docker/config/service.json @@ -23,6 +23,11 @@ "/home/docker/data/resources/" ] }, + "sources": { + "directories": [ + "/home/docker/data/sources/" + ] + }, "network": { "servers": [ { diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index b0edc31..d4756d8 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -11,7 +11,7 @@ services: - pgrouting environment: - NODE_ENV=debug - command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" + command : "sleep 60000" ports: - 8080:8080 - 9229:9229 diff --git a/src/js/geography/projectionManager.js b/src/js/geography/projectionManager.js index fb1d755..a58c178 100644 --- a/src/js/geography/projectionManager.js +++ b/src/js/geography/projectionManager.js @@ -50,12 +50,12 @@ module.exports = class ProjectionManager { /** * * @function - * @name isAvailable + * @name isProjectionLoaded * @description Savoir si une projection est disponible dans l'instance de proj4 * @param {string} id - ID de la projection * */ - isAvailable (id) { + isProjectionLoaded (id) { if (!id) { return false; @@ -77,12 +77,12 @@ module.exports = class ProjectionManager { /** * * @function - * @name isChecked + * @name isProjectionChecked * @description Savoir si une projection a été validé durant l'étape de vérification * @param {string} id - ID de la projection * */ - isChecked (id) { + isProjectionChecked (id) { if (!id) { return false; @@ -101,6 +101,61 @@ module.exports = class ProjectionManager { } + /** + * + * @function + * @name checkBboxConfiguration + * @description Savoir si une bbox est valide dans une projection donnée + * @param {string} bbox - Bbox à vérifier + * @param {string} projectionId - ID de la projection + * + */ + checkBboxConfiguration (bbox, projectionId) { + + LOGGER.info("Vérification d'une bbox..."); + + if (!bbox) { + LOGGER.error("Aucune bbox fournie"); + return false; + } + + if (typeof(bbox) !== "string") { + LOGGER.error("La bbox fournie n'est pas une chaine de caracteres"); + return false; + } + + let regex = new RegExp("^(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*)$"); + if (!regex.test(bbox)) { + LOGGER.error("La bbox n'est pas correctement formattée"); + return false; + } + + let bboxArray = new Array(4); + try { + bboxArray = regex.exec(bbox); + } catch(error) { + LOGGER.error("Impossible d'analyser la bbox"); + LOGGER.error(error); + return false; + } + + if (bboxArray[0] >= bboxArray[2]) { + LOGGER.error("Mauvaise configuration : Xmin est supérieur ou égal à Xmax"); + return false; + } + + if (bboxArray[1] >= bboxArray[3]) { + LOGGER.error("Mauvaise configuration : Ymin est supérieur ou égal à Ymax"); + return false; + } + + // TODO : vérifier la cohérence avec la projection indiquée + + LOGGER.info("Vérification de la bbox terminée"); + return true; + + } + /** * * @function diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index b6a685d..897e3f5 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -236,21 +236,14 @@ module.exports = class resourceManager { for (let i = 0; i < resourceJsonObject.resource.sources.length; i++ ) { - let sourceJsonObject = resourceJsonObject.resource.sources[i]; - if (!this._sourceManager.checkSourceConfiguration(sourceJsonObject)) { - LOGGER.error("La ressource contient une source invalide."); + let sourceId = resourceJsonObject.resource.sources[i]; + if (!this._sourceManager.isCheckedSourceAvailable(sourceId)) { + LOGGER.error("La ressource contient une source non disponible."); return false; } else { - // on stocke l'id de la ressource pour cette source donnée - this._sourceManager.saveCheckedSource(sourceJsonObject); + // TODO : on stocke l'id de la ressource pour cette source donnée } - // Lien avec la topologie - // TODO: vérifier que le type de la topologie soit cohérent avec le type de la source - - // On stocke la correspondance entre une source et la topologie dont elle dérive - this._sourceManager.sourceTopology[sourceJsonObject.id] = resourceJsonObject.resource.topology.id; - } } @@ -396,10 +389,10 @@ module.exports = class resourceManager { } // Création des sources associées - LOGGER.info("Chargement des sources associées..."); + LOGGER.info("Vérification du chargement des sources associées..."); for (let i = 0; i < resourceJsonObject.resource.sources.length; i++) { - if (!this._sourceManager.loadSourceConfiguration(resourceJsonObject.resource.sources[i], currentTopology)) { - LOGGER.error("Impossible de créer la source associée à la ressource : " + resourceJsonObject.resource.sources[i].id); + if (!this._sourceManager.isLoadedSourceAvailable(resourceJsonObject.resource.sources[i])) { + LOGGER.error("La source associée à la ressource n'est pas chargée : " + resourceJsonObject.resource.sources[i].id); return false; } } diff --git a/src/js/service/service.js b/src/js/service/service.js index 899ca66..66344c3 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -52,7 +52,7 @@ module.exports = class Service { this._topologyManager = new TopologyManager(this._baseManager, this._projectionManager); // Manager des sources du service. - this._sourceManager = new SourceManager(); + this._sourceManager = new SourceManager(this._projectionManager); // Manager des ressources du service. this._resourceManager = new ResourceManager(this._sourceManager, this._operationManager, this._topologyManager); @@ -426,6 +426,63 @@ module.exports = class Service { } + // Information sur les sources + LOGGER.debug("Vérification des informations sur les sources"); + + if (!userConfiguration.application.sources) { + LOGGER.fatal("Mauvaise configuration: Objet 'application:sources' manquant !"); + return false; + } else { + // Dossier contenant les fichiers de sources + if (!userConfiguration.application.sources.directories) { + LOGGER.fatal("Mauvaise configuration: Champ 'application:sources:directories' manquant !"); + return false; + } else { + + let sourcesDirectories = userConfiguration.application.sources.directories; + // On vérifie que c'est un tableau non vide + if (!Array.isArray(sourcesDirectories)) { + LOGGER.error("Mauvaise configuration: Champ 'application:sources:directories' n'est pas un tableau !"); + return false; + } + if (sourcesDirectories.length === 0) { + LOGGER.error("Mauvaise configuration: Champ 'application:sources:directories' est un tableau vide !"); + return false; + } + + let oneValidDir = false; + + for (let i = 0; i < sourcesDirectories.length; i++) { + + // On vérifie que le dossier existe et qu'il contient des fichiers de description des ressources + if (sourcesDirectories[i] === "") { + LOGGER.warn("Mauvaise configuration: Champ 'application:sources:directories' contient un élément vide"); + continue; + } else { + + let directory = path.resolve(path.dirname(userConfigurationPath), sourcesDirectories[i]); + + if (!(await this._sourceManager.checkSourceDirectory(directory))) { + LOGGER.error("Le dossier " + directory + " contient des sources dont la vérification a échoué"); + return false; + } else { + LOGGER.info("Le dossier " + directory + " est vérifié et suffisamment validé pour continuer"); + oneValidDir = true; + } + + } + + } + + if (!oneValidDir) { + LOGGER.error("Aucun dossier de source n'a été validé"); + return false; + } + + } + + } + // Information sur les ressources LOGGER.debug("Vérification des informations sur les ressources"); @@ -683,6 +740,36 @@ module.exports = class Service { return false; } + // Chargement des sources + LOGGER.info("Chargement des sources..."); + + for (let i = 0; i < this._configuration.application.sources.directories.length; i++) { + + let sourceDirectory = ""; + + try { + sourceDirectory = path.resolve(path.dirname(this._configurationPath), this._configuration.application.sources.directories[i]); + LOGGER.info("Dossier de sources : " + sourceDirectory); + } catch (error) { + LOGGER.error("Impossible d'obtenir le chemin absolu du dossier de sources: " + this._configuration.application.sources.directories[i]); + LOGGER.error(error); + continue; + } + + if (!this._sourceManager.loadSourceDirectory(sourceDirectory)) { + LOGGER.error("Impossible de charger correctement le dossier de sources " + sourceDirectory); + } else { + // On va continuer + LOGGER.info("Les sources du dossier " + sourceDirectory + " sont chargées dans la mesure du possible") + } + + } + + if (this._sourceManager.source.length === 0) { + LOGGER.fatal("Aucune ressource n'a pu etre chargee"); + return false; + } + // Chargement des ressources LOGGER.info("Chargement des ressources..."); diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index 86d365d..7c7ac7b 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -21,7 +21,7 @@ module.exports = class sourceManager { * @description Constructeur de la classe sourceManager * */ - constructor() { + constructor(projectionManager) { // Liste des ids des sources chargées par le manager this._loadedSourceId = new Array(); @@ -38,14 +38,15 @@ module.exports = class sourceManager { // Descriptions des sources vérifiées par le manager this._checkedSourceConfiguration = {}; - // Correspondance entre l'id d'une source et l'id de la topologie dont elle dérive - this._sourceTopology = {}; + // Manager des projections + this._projectionManager = projectionManager; // Correspondance entre les sources et les opérations possibles this._operationsByType = { "osrm": ["nearest", "route"], "pgr": ["route", "isochrone"], - "smartrouting": ["route", "isochrone"] + "smartrouting": ["route", "isochrone"], + "valhalla": ["route", "isochrone"] }; } @@ -53,64 +54,151 @@ module.exports = class sourceManager { /** * * @function - * @name get loadedSourceId + * @name get source * @description Récupérer l'ensemble des ids de sources chargées * */ - get loadedSourceId() { - return this._loadedSourceId; + get source() { + return this._source; } /** * * @function - * @name get source - * @description Récupérer l'ensemble des ids de sources chargées + * @name get operationsByType + * @description Récupérer l'ensemble des opérations possibles par type de source * */ - get source() { - return this._source; + get operationsByType() { + return this._operationsByType; } /** * * @function - * @name get sourceTopology - * @description Récupérer la correspondance source/topologie + * @name isCheckedSourceAvailable + * @description Fonction utilisée pour vérifier si une source a été vérifiée + * @param {string} id - Id de la source + * @return {boolean} * */ - get sourceTopology() { - return this._sourceTopology; + + isCheckedSourceAvailable(id) { + + if (this._checkedSourceId.length === 0) { + return false; + } + + for (let i = 0; i < this._checkedSourceId.length; i++) { + if (this._checkedSourceId[i] === id) { + return true; + } + } + return false; + } /** * * @function - * @name get operationsByType - * @description Récupérer l'ensemble des opérations possibles par type de source + * @name isLoadedSourceAvailable + * @description Fonction utilisée pour vérifier si une source a été chargée + * @param {string} id - Id de la source + * @return {boolean} * */ - get operationsByType() { - return this._operationsByType; + + isLoadedSourceAvailable(id) { + + if (this._loadedSourceId.length === 0) { + return false; + } + + for (let i = 0; i < this._loadedSourceId.length; i++) { + if (this._loadedSourceId[i] === id) { + return true; + } + } + return false; + } /** * * @function - * @name set loadedSourceId - * @description Attribuer l'ensemble des ids de sources - * @param {table} list - État de la connexion + * @name checkSourceDirectory + * @description Fonction utilisée pour vérifier un dossier contenant des sources. + * @param {string} directory - Dossier qui contient les configurations des ressources + * @return {boolean} * */ - set loadedSourceId(list) { - this._loadedSourceId = list; + + checkSourceDirectory(directory) { + + LOGGER.info("Vérification d'un dossier de sources..."); + LOGGER.info("Nom du dossier: " + directory); + + if (fs.existsSync(directory)) { + + let fileList = new Array(); + try { + fileList = fs.readdirSync(directory); + } catch(error) { + LOGGER.error("Impossible de lire le dossier :"); + LOGGER.error(error); + return false; + } + + if (fileList.length === 0) { + LOGGER.warn("Le dossier " + directory + " est vide"); + return false; + } + + for (let i = 0; i < fileList.length; i++) { + + let source = fileList[i]; + let sourceFile = ""; + try { + sourceFile = directory + "/" + source; + fs.accessSync(sourceFile, fs.constants.R_OK); + } catch (err) { + LOGGER.error("Le fichier de source ne peut etre lu: " + sourceFile); + } + + let sourceConf = {}; + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + sourceConf = JSON.parse(fs.readFileSync(sourceFile)); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de source: " + sourceFile); + LOGGER.error(error); + return false; + } + + if (!(await this.checkSourceConfiguration(sourceConf))) { + LOGGER.error("La source décrite dans le fichier " + sourceFile + " est mal configuée"); + return false; + } else { + this._checkedSourceId.push(sourceConf.source.id); + } + + } + + LOGGER.info("Vérification du dossier de sources terminée"); + return true; + + } else { + LOGGER.error("Mauvaise configuration: Le dossier n'existe pas: " + directory ); + return false; + } + } /** * * @function * @name checkSourceConfiguration - * @description Fonction utilisée pour vérifier la partie source d'un fichier de description d'une ressource. + * @description Fonction utilisée pour vérifier la configuration d'une source. * @param {json} sourceJsonObject - Description JSON de la source * @return {boolean} * @@ -122,7 +210,7 @@ module.exports = class sourceManager { // ID if (!sourceJsonObject.id) { - LOGGER.error("La ressource contient une source sans id."); + LOGGER.error("Mauvaise configuration: source.id absent"); return false; } else { // On vérifie que l'id n'est pas déjà chargé. @@ -175,128 +263,70 @@ module.exports = class sourceManager { // Type if (!sourceJsonObject.type) { - LOGGER.error("La ressource contient une source sans type."); + LOGGER.error("Mauvaise configuration: source.type absent"); return false; } else { - // Vérification que le type est valide puis vérification spécifique à chaque type - let available = false; - // La partie délimitée peut être copié-collée pour ajouter un nouveau type. - // Il ne reste plus qu'à créer la fonction de vérification correspondante. - //------ OSRM - if (sourceJsonObject.type === "osrm") { - available = true; - LOGGER.info("Source osrm."); - // On vérifie que le module osrm est disponible - try { - let osrmTest = require('osrm'); - } catch(error) { - LOGGER.error("Le module osrm n'est pas disponible mais une source osrm est proposée dans la configuration."); - return false; - } + LOGGER.debug("Type présent"); - if (!this.checkSourceOsrm(sourceJsonObject)) { - LOGGER.error("Erreur lors de la verification de la source osrm."); - return false; - } else { - // il n'y a eu aucun problème, la source est correctement configurée. - } + // Vérification que le type est valide puis vérification spécifique à chaque type + + if (!Object.keys(this._operationsByType).includes(sourceJsonObject.type)) { + LOGGER.error("La source indique un type non disponible : " + sourceJsonObject.type); + return false; } else { - // On va voir si c'est un autre type. + LOGGER.debug("Le type présent est disponible : " + sourceJsonObject.type); } - //------ OSRM - //------ PGR - if (sourceJsonObject.type === "pgr") { - available = true; - LOGGER.info("Source pgrouting."); - // On vérifie que le module pg est disponible - try { - let { poolTest } = require('pg'); - } catch(error) { - LOGGER.error("Le module pg n'est pas disponible mais une source pgrouting est proposée dans la configuration."); - return false; - } + let validation = false; - if (!this.checkSourcePgr(sourceJsonObject)) { - LOGGER.error("Erreur lors de la verification de la source pgr."); - return false; - } else { - // il n'y a eu aucun problème, la ressource est correctement configurée. - } + if (sourceJsonObject.type === "osrm") { + validation = this.checkSourceOsrm(sourceJsonObject); + } else if (sourceJsonObject.type === "pgr") { + validation = this.checkSourcePgr(sourceJsonObject); + } else if (sourceJsonObject.type === "valhalla") { + validation = this.checkSourceValhalla(sourceJsonObject); + } else if (sourceJsonObject.type === "smartrouting") { + validation = this.checkSourceSmartrouting(sourceJsonObject); } else { - // On va voir si c'est un autre type. + LOGGER.error("La source indique un type invalide : " + sourceJsonObject.type); + return false; } - //------ PGR - //------ SMARTROUTING - if (sourceJsonObject.type === "smartrouting") { - available = true; - LOGGER.info("Source smartrouting."); - - if (!this.checkSourceSmartrouting(sourceJsonObject)) { - LOGGER.error("Erreur lors de la verification de la source smartrouting."); - return false; - } else { - // il n'y a eu aucun problème, la ressource est correctement configurée. - } + + if (!validation) { + LOGGER.error("Erreur lors de la verification de la source"); + return false; } else { - // On va voir si c'est un autre type. + LOGGER.debug("Aucune erreur lors de la vérification de la source"); } - //------ SMARTROUTING - //------ VALHALLA - if (sourceJsonObject.type === "valhalla") { - available = true; - LOGGER.info("Source valhalla."); - - let operationFound = false; - - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("route")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("route", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - - // On vérifie que les opérations possibles sur ce type de source soient disponibles dans l'instance du service - if (operationManager.verifyAvailabilityOperation("isochrone")) { - // On vérifie que les opérations possibles sur ce type de source soient disponibles pour la ressource - if (operationManager.isAvailableInTable("isochrone", resourceOperationTable)) { - operationFound = true; - } else { - // on continue pour voir la suite - } - } else { - // on continue pour voir la suite - } - if (!operationFound) { - LOGGER.error("Le service ne propose pas d'operations disponibles pour ce type de source (ex. route, isochrone), il n'est donc pas possible de charger cette source."); - return false; - } + } - if (!this.checkSourceValhalla(sourceJsonObject)) { - LOGGER.error("Erreur lors de la verification de la source valhalla."); - return false; - } else { - // il n'y a eu aucun problème, la ressource est correctement configurée. - } - } else { - // On va voir si c'est un autre type. + // Projection + if (!sourceJsonObject.projection) { + LOGGER.error("Mauvaise configuration: source.projection absent"); + return false; + } else { + // Vérification de la projection + if (!this._projectionManager.isProjectionChecked(sourceJsonObject.projection)) { + LOGGER.error("La source indique une projection non disponible sur le service: " + topologyJsonDescription.projection); + return false; } - //------ VALHALLA + } - // Si ce n'est aucun type valide, on renvoie une erreur. - if (!available) { - LOGGER.error("La source indique un type invalide: " + sourceJsonObject.type); + // Bbox + if (!sourceJsonObject.bbox) { + LOGGER.error("Mauvaise configuration: source.bbox absent"); + return false; + } else { + // Vérification de la bbox + if (!this._projectionManager.checkBboxConfiguration(sourceJsonObject.bbox, sourceJsonObject.projection)) { + LOGGER.error("La source indique une projection non disponible sur le service: " + topologyJsonDescription.projection); return false; } } + LOGGER.info("Fin de la verification de la source."); return true; @@ -316,6 +346,14 @@ module.exports = class sourceManager { LOGGER.info("Verification de la source osrm..."); + // On vérifie que le module osrm est disponible + try { + let osrmTest = require('osrm'); + } catch(error) { + LOGGER.error("Le module osrm n'est pas disponible mais une source osrm est proposée dans la configuration."); + return false; + } + // Storage if (!sourceJsonObject.storage) { LOGGER.error("La ressource contient une source sans stockage."); @@ -384,6 +422,14 @@ module.exports = class sourceManager { LOGGER.info("Verification de la source pgr..."); + // On vérifie que le module pg est disponible + try { + let { poolTest } = require('pg'); + } catch(error) { + LOGGER.error("Le module pg n'est pas disponible mais une source pgrouting est proposée dans la configuration."); + return false; + } + // Storage if (!sourceJsonObject.storage) { LOGGER.error("La ressource contient une source sans stockage."); @@ -592,18 +638,61 @@ module.exports = class sourceManager { } + /** + * + * @function + * @name loadSourceDirectory + * @description Fonction utilisée pour créer les sources contenues dans un dossier. + * @param {string} directory - Dossier qui contient les configurations des ressources + * @return {boolean} + * + */ + + loadSourceDirectory(directory) { + + // Pour chaque fichier du dossier des sources, on crée une source + let files = fs.readdirSync(directory).filter( (file) => { + return path.extname(file).toLowerCase() === ".source"; + }); + + for (let fileName of files) { + + let sourceFile = directory + "/" + fileName; + LOGGER.info("Chargement de la source : " + sourceFile); + + // Récupération du contenu en objet pour vérification puis création de la source + let sourceContent = {}; + try { + sourceContent = JSON.parse(fs.readFileSync(sourceFile)); + } catch (error) { + LOGGER.error(error); + LOGGER.error("Erreur lors de la lecture de la source: " + sourceFile); + } + + // Création de la ressource + if (!this.loadSourceConfiguration(sourceContent)) { + LOGGER.error("La source configurée dans le fichier " + sourceFile + " n'a pas pu être chargée"); + } else { + LOGGER.info("Source chargée : " + sourceFile); + } + + } + + return true; + + } + /** * * @function * @name loadSourceConfiguration * @description Fonction utilisée pour créer une source. * @param {json} sourceJsonObject - Description JSON de la source - * @param {Topology} topology - Instance de la classe Topology * @return {boolean} * */ - loadSourceConfiguration(sourceJsonObject, topology) { + loadSourceConfiguration(sourceJsonObject) { LOGGER.info("Creation de la source: " + sourceJsonObject.id); diff --git a/src/js/topology/topologyManager.js b/src/js/topology/topologyManager.js index 0f0a5df..c3ba6b9 100644 --- a/src/js/topology/topologyManager.js +++ b/src/js/topology/topologyManager.js @@ -150,7 +150,7 @@ module.exports = class topologyManager { return false; } else { // Vérification de la projection - if (!this._projectionManager.isChecked(topologyJsonDescription.projection)) { + if (!this._projectionManager.isProjectionChecked(topologyJsonDescription.projection)) { LOGGER.error("La topologie indique une projection non disponible sur le service: " + topologyJsonDescription.projection); return false; } From 8f678fcf580af4d25202c9ddcfca71c8569586f7 Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 3 Nov 2022 09:23:19 +0100 Subject: [PATCH 12/93] WIP: suppression des topologies --- changelog.md | 2 + .../configuration/sources/pgrSource.json | 196 +++++++ src/js/resources/resourceManager.js | 36 +- src/js/service/service.js | 11 +- src/js/sources/osrmSource.js | 31 +- src/js/sources/pgrSource.js | 148 +++-- src/js/sources/smartroutingSource.js | 3 +- src/js/sources/source.js | 26 +- src/js/sources/sourceManager.js | 416 ++++++++++---- src/js/sources/valhallaSource.js | 46 +- src/js/topology/dbTopology.js | 137 ----- src/js/topology/osmTopology.js | 47 -- src/js/topology/topology.js | 99 ---- src/js/topology/topologyManager.js | 515 ------------------ src/js/utils/storageManager.js | 84 --- test/integration/readme.md | 13 +- 16 files changed, 675 insertions(+), 1135 deletions(-) create mode 100644 documentation/configuration/sources/pgrSource.json delete mode 100644 src/js/topology/dbTopology.js delete mode 100644 src/js/topology/osmTopology.js delete mode 100644 src/js/topology/topology.js delete mode 100644 src/js/topology/topologyManager.js delete mode 100644 src/js/utils/storageManager.js diff --git a/changelog.md b/changelog.md index 7659939..42c3f18 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,8 @@ ADDED: CHANGED: - L'option --configCheck au démarrage de Road2 n'a plus exactement le même comportement. - Le fichier server.json permet maintenant de configurer l'administrateur et donc n'a plus le même contenu. Ce dernier est dans service.json. + - Les sources ne sont plus configurées dans le même fichier que les ressources. Chaque source est configurée dans son fichier. L'ensemble est placé dans un dossier de sources. Il peut y en avoir plusieurs. + - Les sources PGRouting et Valhalla ne sont plus configurées de la même manière : chaque source de ces types peut contenir plusieurs coûts. # 1.1.2 diff --git a/documentation/configuration/sources/pgrSource.json b/documentation/configuration/sources/pgrSource.json new file mode 100644 index 0000000..4a68be5 --- /dev/null +++ b/documentation/configuration/sources/pgrSource.json @@ -0,0 +1,196 @@ +{ + "id": "bduni-idf-car-fastest-pgr", + "description":"test", + "type": "pgr", + "projection": "EPSG:4326", + "bbox": "1.7,48.4,3.3,49.1", + "storage": { + "base": { + "dbConfig": "/home/docker/data/output_base.json", + "schema": "public", + "attributes": [ + { + "key": "name", + "column": "way_names", + "default": "false" + }, + { + "key": "nom_1_gauche", + "column": "nom_1_gauche", + "default": "true" + }, + { + "key": "nom_1_droite", + "column": "nom_1_droite", + "default": "true" + }, + { + "key": "cpx_numero", + "column": "cpx_numero", + "default": "true" + }, + { + "key": "cpx_toponyme_route_nommee", + "column": "cpx_toponyme_route_nommee", + "default": "true" + }, + { + "key": "cleabs", + "column": "cleabs", + "default": "false" + }, + { + "key": "nature", + "column": "nature", + "default": "false" + }, + { + "key": "importance", + "column": "importance", + "default": "false" + }, + { + "key": "position_par_rapport_au_sol", + "column": "position_par_rapport_au_sol", + "default": "false" + }, + { + "key": "nombre_de_voies", + "column": "nombre_de_voies", + "default": "false" + }, + { + "key": "largeur_de_chaussee", + "column": "largeur_de_chaussee", + "default": "false" + }, + { + "key": "itineraire_vert", + "column": "itineraire_vert", + "default": "false" + }, + { + "key": "sens_de_circulation", + "column": "sens_de_circulation", + "default": "false" + }, + { + "key": "bande_cyclable", + "column": "bande_cyclable", + "default": "false" + }, + { + "key": "reserve_aux_bus", + "column": "reserve_aux_bus", + "default": "false" + }, + { + "key": "urbain", + "column": "urbain", + "default": "false" + }, + { + "key": "vitesse_moyenne_vl", + "column": "vitesse_moyenne_vl", + "default": "false" + }, + { + "key": "acces_vehicule_leger", + "column": "acces_vehicule_leger", + "default": "false" + }, + { + "key": "acces_pieton", + "column": "acces_pieton", + "default": "false" + }, + { + "key": "nature_de_la_restriction", + "column": "nature_de_la_restriction", + "default": "false" + }, + { + "key": "restriction_de_hauteur", + "column": "restriction_de_hauteur", + "default": "false" + }, + { + "key": "restriction_de_poids_total", + "column": "restriction_de_poids_total", + "default": "false" + }, + { + "key": "restriction_de_poids_par_essieu", + "column": "restriction_de_poids_par_essieu", + "default": "false" + }, + { + "key": "restriction_de_largeur", + "column": "restriction_de_largeur", + "default": "false" + }, + { + "key": "restriction_de_longueur", + "column": "restriction_de_longueur", + "default": "false" + }, + { + "key": "matieres_dangereuses_interdites", + "column": "matieres_dangereuses_interdites", + "default": "false" + }, + { + "key": "insee_commune_gauche", + "column": "insee_commune_gauche", + "default": "false" + }, + { + "key": "insee_commune_droite", + "column": "insee_commune_droite", + "default": "false" + }, + { + "key": "cpx_numero_route_europeenne", + "column": "cpx_numero_route_europeenne", + "default": "false" + }, + { + "key": "cpx_classement_administratif", + "column": "cpx_classement_administratif", + "default": "false" + }, + { + "key": "cpx_gestionnaire", + "column": "cpx_gestionnaire", + "default": "false" + } + ] + } + }, + "costs": [ + { + "profile": "car", + "optimization": "fastest", + "costColumn": "cost_s_car", + "rcostColumn": "reverse_cost_s_car" + }, + { + "profile": "car", + "optimization": "shortest", + "costColumn": "cost_s_car", + "rcostColumn": "reverse_cost_s_car" + }, + { + "profile": "pedestrian", + "optimization": "fastest", + "costColumn": "cost_s_car", + "rcostColumn": "reverse_cost_s_car" + }, + { + "profile": "pedestrian", + "optimization": "shortest", + "costColumn": "cost_s_car", + "rcostColumn": "reverse_cost_s_car" + } + ] + } diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index 897e3f5..9a80015 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -20,7 +20,7 @@ module.exports = class resourceManager { * @description Constructeur de la classe resourceManager * */ - constructor(sourceManager, operationManager, topologyManager) { + constructor(sourceManager, operationManager) { // Liste des ids des ressources chargées par le manager this._loadedResourceId = new Array(); @@ -34,9 +34,6 @@ module.exports = class resourceManager { // Liste des types de ressource gérées par le manager this._availableResourceTypes = ["pgr", "smartpgr","osrm","valhalla"]; - // Manager de topology - this._topologyManager = topologyManager; - // Manager de source this._sourceManager = sourceManager; @@ -66,7 +63,7 @@ module.exports = class resourceManager { * */ - async checkResourceDirectory(directory) { + checkResourceDirectory(directory) { LOGGER.info("Vérification d'un dossier de ressources..."); LOGGER.info("Nom du dossier: " + directory); @@ -108,7 +105,7 @@ module.exports = class resourceManager { return false; } - if (!(await this.checkResourceConfiguration(resourceConf))) { + if (!this.checkResourceConfiguration(resourceConf)) { LOGGER.error("La ressource décrite dans le fichier " + resourceFile + " est mal configuée"); return false; } else { @@ -137,7 +134,7 @@ module.exports = class resourceManager { * */ - async checkResourceConfiguration(resourceJsonObject) { + checkResourceConfiguration(resourceJsonObject) { LOGGER.info("Verification de la configuration d'une ressource..."); @@ -213,19 +210,6 @@ module.exports = class resourceManager { return false; } - // Topology - if (!resourceJsonObject.resource.topology) { - LOGGER.error("La ressource ne contient pas de topologie."); - return false; - } else { - if (!(await this._topologyManager.checkTopologyConfiguration(resourceJsonObject.resource.topology))) { - LOGGER.error("La ressource contient une topologie incorrecte."); - return false; - } else { - this._topologyManager.saveCheckedTopology(resourceJsonObject.resource.topology); - } - } - // Sources if (!resourceJsonObject.resource.sources) { LOGGER.error("La ressource ne contient pas de sources."); @@ -378,17 +362,7 @@ module.exports = class resourceManager { // C'est la première ressource créée } - // Création de la topology associée - LOGGER.info("Chargement de la topology associé..."); - let currentTopology = {}; - if (!this._topologyManager.loadTopologyConfiguration(resourceJsonObject.resource.topology)) { - LOGGER.error("Impossible de créer la topology associée à la ressource"); - return false; - } else { - currentTopology = this._topologyManager.getTopology(resourceJsonObject.resource.topology.id); - } - - // Création des sources associées + // Vérification des sources associées LOGGER.info("Vérification du chargement des sources associées..."); for (let i = 0; i < resourceJsonObject.resource.sources.length; i++) { if (!this._sourceManager.isLoadedSourceAvailable(resourceJsonObject.resource.sources[i])) { diff --git a/src/js/service/service.js b/src/js/service/service.js index 66344c3..85f4e81 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -10,7 +10,6 @@ const ResourceManager = require('../resources/resourceManager'); const SourceManager = require('../sources/sourceManager'); const OperationManager = require('../operations/operationManager'); const BaseManager = require('../base/baseManager'); -const TopologyManager = require('../topology/topologyManager'); const ProjectionManager = require('../geography/projectionManager'); const ServerManager = require('../server/serverManager'); const LogManager = require('../utils/logManager'); @@ -48,14 +47,11 @@ module.exports = class Service { // Manager des projections this._projectionManager = new ProjectionManager(); - // Manager des topologies du service - this._topologyManager = new TopologyManager(this._baseManager, this._projectionManager); - // Manager des sources du service. - this._sourceManager = new SourceManager(this._projectionManager); + this._sourceManager = new SourceManager(this._projectionManager, this._baseManager); // Manager des ressources du service. - this._resourceManager = new ResourceManager(this._sourceManager, this._operationManager, this._topologyManager); + this._resourceManager = new ResourceManager(this._sourceManager, this._operationManager); // Manager des apis du service this._apisManager = new ApisManager(); @@ -520,7 +516,7 @@ module.exports = class Service { let directory = path.resolve(path.dirname(userConfigurationPath), resourcesDirectories[i]); - if (!(await this._resourceManager.checkResourceDirectory(directory))) { + if (!this._resourceManager.checkResourceDirectory(directory)) { LOGGER.error("Le dossier " + directory + " contient des ressources dont la vérification a échoué"); return false; } else { @@ -654,7 +650,6 @@ module.exports = class Service { this._operationManager.flushCheckedOperation(); this._resourceManager.flushCheckedResource(); this._sourceManager.flushCheckedSource(); - this._topologyManager.flushCheckedTopology(); this._serverManager.flushCheckedServer(); LOGGER.info("Verification terminee."); diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index ea39ec5..3115c92 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -39,13 +39,12 @@ module.exports = class osrmSource extends Source { * @name constructor * @description Constructeur de la classe osrmSource * @param {json} sourceJsonObject - Description de la source en json - * @param {topology} topology - Instance de la classe Topology * */ - constructor(sourceJsonObject, topology) { + constructor(sourceJsonObject) { // Constructeur parent - super(sourceJsonObject.id,sourceJsonObject.type, topology); + super(sourceJsonObject.id, "osrm", sourceJsonObject.description, sourceJsonObject.projection, sourceJsonObject.bbox); // Stockage de la configuration this._configuration = sourceJsonObject; @@ -173,15 +172,15 @@ module.exports = class osrmSource extends Source { // Coordonnées let coordinatesTable = new Array(); // start - coordinatesTable.push(request.start.getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.start.getCoordinatesIn(super.projection)); // intermediates if (request.intermediates.length !== 0) { for (let i = 0; i < request.intermediates.length; i++) { - coordinatesTable.push(request.intermediates[i].getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.intermediates[i].getCoordinatesIn(super.projection)); } } // end - coordinatesTable.push(request.end.getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.end.getCoordinatesIn(super.projection)); LOGGER.debug("coordinates:"); LOGGER.debug(coordinatesTable); @@ -286,7 +285,7 @@ module.exports = class osrmSource extends Source { // --- let osrmRequest = {}; - osrmRequest.coordinates = [request.coordinates.getCoordinatesIn(this.topology.projection)]; + osrmRequest.coordinates = [request.coordinates.getCoordinatesIn(super.projection)]; osrmRequest.number = request.number; // --- @@ -400,10 +399,10 @@ module.exports = class osrmSource extends Source { let askedProjection = routeRequest.start.projection; LOGGER.debug("asked projection: " + askedProjection); - LOGGER.debug("topology projection: " + this.topology.projection); + LOGGER.debug("source projection: " + super.projection); // start - start = new Point(osrmResponse.waypoints[0].location[0], osrmResponse.waypoints[0].location[1], this.topology.projection); + start = new Point(osrmResponse.waypoints[0].location[0], osrmResponse.waypoints[0].location[1], super.projection); if (!start.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of start in OSRM response. "); } else { @@ -412,7 +411,7 @@ module.exports = class osrmSource extends Source { } // end - end = new Point(osrmResponse.waypoints[osrmResponse.waypoints.length-1].location[0], osrmResponse.waypoints[osrmResponse.waypoints.length-1].location[1], this.topology.projection); + end = new Point(osrmResponse.waypoints[osrmResponse.waypoints.length-1].location[0], osrmResponse.waypoints[osrmResponse.waypoints.length-1].location[1], super.projection); if (!end.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of end in OSRM response. "); } else { @@ -439,7 +438,7 @@ module.exports = class osrmSource extends Source { let currentOsrmRoute = osrmResponse.routes[i]; // On commence par créer l'itinéraire avec les attributs obligatoires - routes[i] = new Route( new Line(currentOsrmRoute.geometry, "geojson", this._topology.projection) ); + routes[i] = new Route( new Line(currentOsrmRoute.geometry, "geojson", super.projection) ); if (!routes[i].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of geometry in OSRM response. "); } else { @@ -465,7 +464,7 @@ module.exports = class osrmSource extends Source { let currentOsrmRouteLeg = currentOsrmRoute.legs[j]; - let legStart = new Point(osrmResponse.waypoints[j].location[0], osrmResponse.waypoints[j].location[1], this.topology.projection); + let legStart = new Point(osrmResponse.waypoints[j].location[0], osrmResponse.waypoints[j].location[1], super.projection); if (!legStart.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of leg start in OSRM response. "); } else { @@ -473,7 +472,7 @@ module.exports = class osrmSource extends Source { LOGGER.debug(legStart); } - let legEnd = new Point(osrmResponse.waypoints[j+1].location[0], osrmResponse.waypoints[j+1].location[1], this.topology.projection); + let legEnd = new Point(osrmResponse.waypoints[j+1].location[0], osrmResponse.waypoints[j+1].location[1], super.projection); if (!legEnd.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of leg end in OSRM response. "); } else { @@ -496,7 +495,7 @@ module.exports = class osrmSource extends Source { LOGGER.debug("Step number " + k + " of portion number " + j + " for route number " + i); let currentOsrmRouteStep = currentOsrmRouteLeg.steps[k]; - steps[k] = new Step( new Line(currentOsrmRouteStep.geometry, "geojson", this._topology.projection) ); + steps[k] = new Step( new Line(currentOsrmRouteStep.geometry, "geojson", super.projection) ); if (!steps[k].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of step's geometry in OSRM response. "); } else { @@ -561,7 +560,7 @@ module.exports = class osrmSource extends Source { let askedProjection = nearestRequest.coordinates.projection; LOGGER.debug("asked projection: " + askedProjection); - LOGGER.debug("topology projection: " + this.topology.projection); + LOGGER.debug("source projection: " + super.projection); // Création de la réponse let nearestResponse = new NearestResponse(nearestRequest.resource, nearestRequest.coordinates); @@ -577,7 +576,7 @@ module.exports = class osrmSource extends Source { if (osrmResponse.waypoints[i].location) { - let pointGeom = new Point(osrmResponse.waypoints[i].location[0], osrmResponse.waypoints[i].location[1], this.topology.projection); + let pointGeom = new Point(osrmResponse.waypoints[i].location[0], osrmResponse.waypoints[i].location[1], super.projection); if (!pointGeom.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of a point in OSRM response. "); } else { diff --git a/src/js/sources/pgrSource.js b/src/js/sources/pgrSource.js index 1f1a68e..a766888 100644 --- a/src/js/sources/pgrSource.js +++ b/src/js/sources/pgrSource.js @@ -36,25 +36,64 @@ module.exports = class pgrSource extends Source { * @name constructor * @description Constructeur de la classe pgrSource * @param{json} sourceJsonObject - Description de la source - * @param{topology} topology - Instance de la classe Topology + * @param{Base} base - Instance de Base pour permettre les requêtes vers le serveur PGRouting * */ - constructor(sourceJsonObject, topology) { + constructor(sourceJsonObject, base) { // Constructeur parent - super(sourceJsonObject.id, "pgr", topology); + super(sourceJsonObject.id, "pgr", sourceJsonObject.description, sourceJsonObject.projection, sourceJsonObject.bbox); // Stockage de la configuration this._configuration = sourceJsonObject; - // Coût - this._cost = sourceJsonObject.storage.costColumn; + // Base de données PGRouting + this._base = base; - // Coût inverse - this._reverseCost = sourceJsonObject.storage.rcostColumn; + // Schéma contenant les données dans la base + this._schema = sourceJsonObject.storage.base.schema; - // Profil - this._profile = sourceJsonObject.cost.profile; + // Attributs par défaut sur les voies dans la base + this._defaultAttributes = new Array(); + + // Attributs disponibles sur les voies dans la base + this._otherAttributes = new Array(); + + // Création des tableaux d'attributs + for (let i = 0; i < sourceJsonObject.storage.base.attributes.length; i++) { + let curAttribute = sourceJsonObject.storage.base.attributes[i]; + if (curAttribute.default === "true") { + this._defaultAttributes.push(curAttribute); + } else if (curAttribute.default === "false") { + this._otherAttributes.push(curAttribute); + } else { + // cela ne doit pas arriver + } + } + + // stockage des attributs dans des tableaux pour le traitement des requêtes + this._defaultAttributesTable = new Array(); + this._defaultAttributesKeyTable = new Array(); + for (let i = 0; i < this._defaultAttributes.length; i++) { + this._defaultAttributesTable.push("'" + this._defaultAttributes[i].column + "'"); + this._defaultAttributesKeyTable.push(this._defaultAttributes[i].key); + } + + // stockage des attributs par défaut en une chaîne de caractères pour le traitement des requêtes + this._defaultAttributesString = this._defaultAttributesTable.join(","); + + // Coûts disponibles + this._costs = {}; + + // Initialisation des coûts disponibles + for (let i = 0; i < sourceJsonObject.costs.length; i++) { + Object.defineProperty(this._costs, sourceJsonObject.costs[i].profile, { value: new Object(), configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile], sourceJsonObject.costs[i].optimization, { value: new Object(), configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].optimization], "costColumn", { value: sourceJsonObject.costs[i].costColumn, configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].optimization], "rcostColumn", { value: sourceJsonObject.costs[i].rcostColumn, configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].costType], "costColumn", { value: sourceJsonObject.costs[i].costColumn, configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].costType], "rcostColumn", { value: sourceJsonObject.costs[i].rcostColumn, configurable: true, enumerable: true, writable: true }); + } } @@ -90,12 +129,12 @@ module.exports = class pgrSource extends Source { */ async connect() { - if (!this._topology.base.connected) { + if (!this._base.connected) { // Connection à la base de données try { - await this._topology.base.connect(); + await this._base.connect(); this._connected = true; } catch (err) { @@ -124,11 +163,11 @@ module.exports = class pgrSource extends Source { LOGGER.info("Tentative de deconnection de la base..."); - if (this._topology.base.connected) { + if (this._base.connected) { try { - await this._topology.base.disconnect(); + await this._base.disconnect(); LOGGER.info("Deconnection de la base effectuee"); this._connected = false; @@ -181,15 +220,15 @@ module.exports = class pgrSource extends Source { // Coordonnées // start - coordinatesTable.push(request.start.getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.start.getCoordinatesIn(super.projection)); // intermediates if (request.intermediates.length !== 0) { for (let i = 0; i < request.intermediates.length; i++) { - coordinatesTable.push(request.intermediates[i].getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.intermediates[i].getCoordinatesIn(super.projection)); } } // end - coordinatesTable.push(request.end.getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.end.getCoordinatesIn(super.projection)); LOGGER.debug("coordinates:"); LOGGER.debug(coordinatesTable); @@ -198,11 +237,12 @@ module.exports = class pgrSource extends Source { // --- waysAttributes // attributes est déjà vide, on met les attributs par défaut - attributes = this._topology.defaultAttributesString; + attributes = this._defaultAttributesString; LOGGER.debug("default attributes: " + attributes); // on complète avec les attributs demandés + // TODO : refaire cette partie, et donc la manière dont l'info est stockée dans la classe if (request.waysAttributes.length !== 0) { let requestedAttributes = new Array(); @@ -210,17 +250,17 @@ module.exports = class pgrSource extends Source { // on récupère le nom de la colonne en fonction de l'id de l'attribut demandé let isDefault = false; - for (let j = 0; j < this._topology.defaultAttributes.length; j++) { - if (request.waysAttributes[i] === this._topology.defaultAttributes[j].key) { + for (let j = 0; j < this._defaultAttributes.length; j++) { + if (request.waysAttributes[i] === this._defaultAttributes[j].key) { isDefault = true; break; } } if (!isDefault) { - for (let j = 0; j < this._topology.otherAttributes.length; j++) { - if (request.waysAttributes[i] === this._topology.otherAttributes[j].key) { - requestedAttributes.push("'" + this._topology.otherAttributes[j].column + "'"); + for (let j = 0; j < this._otherAttributes.length; j++) { + if (request.waysAttributes[i] === this._otherAttributes[j].key) { + requestedAttributes.push("'" + this._otherAttributes[j].column + "'"); break; } } @@ -292,20 +332,20 @@ module.exports = class pgrSource extends Source { } // --- - const queryString = `SELECT * FROM ${this._topology.schema}.shortest_path_pgrouting(ARRAY ${JSON.stringify(coordinatesTable)},$1,$2,$3,ARRAY [${attributes}]::text[],$4)`; + const queryString = `SELECT * FROM ${this._schema}.shortest_path_pgrouting(ARRAY ${JSON.stringify(coordinatesTable)},$1,$2,$3,ARRAY [${attributes}]::text[],$4)`; let SQLParametersTable; if (looseConstraintsArray.length === 0) { SQLParametersTable = [ - this._profile, - this._cost, - this._reverseCost, + request.profile, + this._costs[request.profile][request.optimization].costColumn, + this._costs[request.profile][request.optimization].rcostColumn, constraints ]; } else { - let onTheFlyCosts = LooseConstraint.looseConstraintsToSQL(looseConstraintsArray, this._cost, this._reverseCost); + let onTheFlyCosts = LooseConstraint.looseConstraintsToSQL(looseConstraintsArray, this._costs[request.profile][request.optimization].costColumn, this._costs[request.profile][request.optimization].rcostColumn); SQLParametersTable = [ - this._profile, + request.profile, onTheFlyCosts[0], onTheFlyCosts[1], constraints @@ -319,11 +359,11 @@ module.exports = class pgrSource extends Source { LOGGER.debug("SQLParametersTable:"); LOGGER.debug(SQLParametersTable); - if (this._topology.base.pool) { + if (this._base.pool) { try { - this._topology.base.pool.query(queryString, SQLParametersTable, (err, result) => { + this._base.pool.query(queryString, SQLParametersTable, (err, result) => { this.state = "green"; @@ -402,14 +442,14 @@ module.exports = class pgrSource extends Source { LOGGER.debug("no constraints asked"); } - const queryString = `SELECT * FROM ${this._topology.schema}.generateIsochrone(ARRAY ${JSON.stringify(point)}, $1, $2, $3, $4, $5, $6)`; + const queryString = `SELECT * FROM ${this._schema}.generateIsochrone(ARRAY ${JSON.stringify(point)}, $1, $2, $3, $4, $5, $6)`; const SQLParametersTable = [ request.costValue, request.direction, parseInt(request.askedProjection.split(':')[1]), // e.g. Transformer "EPSG:4326" en 4326 (pour PostGIS). - this._configuration.storage.costColumn, - this._configuration.storage.rcostColumn, + this._costs[request.profile][request.costType].costColumn, + this._costs[request.profile][request.costType].rcostColumn, constraints ]; @@ -419,11 +459,11 @@ module.exports = class pgrSource extends Source { LOGGER.debug("SQLParametersTable:"); LOGGER.debug(SQLParametersTable); - if (this._topology.base.pool) { + if (this._base.pool) { try { - this._topology.base.pool.query(queryString, SQLParametersTable, (err, result) => { + this._base.pool.query(queryString, SQLParametersTable, (err, result) => { this.state = "green"; @@ -548,10 +588,10 @@ module.exports = class pgrSource extends Source { LOGGER.debug("attributes management"); // On fait la liste des attributs par défaut - if (this._topology.defaultAttributesKeyTable.length !== 0) { - for (let i = 0; i < this._topology.defaultAttributesKeyTable.length; i++) { - finalAttributesKey.push(this._topology.defaultAttributesKeyTable[i]); - LOGGER.debug(this._topology.defaultAttributesKeyTable[i] + " added"); + if (this._defaultAttributesKeyTable.length !== 0) { + for (let i = 0; i < this._defaultAttributesKeyTable.length; i++) { + finalAttributesKey.push(this._defaultAttributesKeyTable[i]); + LOGGER.debug(this._defaultAttributesKeyTable[i] + " added"); } } else { // il n'y a aucun attribut par défaut @@ -564,18 +604,18 @@ module.exports = class pgrSource extends Source { // on récupère le nom de la colonne en fonction de l'id de l'attribut demandé let isDefault = false; - for (let j = 0; j < this._topology.defaultAttributes.length; j++) { - if (routeRequest.waysAttributes[i] === this._topology.defaultAttributes[j].key) { + for (let j = 0; j < this._defaultAttributes.length; j++) { + if (routeRequest.waysAttributes[i] === this._defaultAttributes[j].key) { isDefault = true; break; } } if (!isDefault) { - for (let j = 0; j < this._topology.otherAttributes.length; j++) { - if (routeRequest.waysAttributes[i] === this._topology.otherAttributes[j].key) { - finalAttributesKey.push(this._topology.otherAttributes[j].key); - LOGGER.debug(this._topology.otherAttributes[j].key + " added"); + for (let j = 0; j < this._otherAttributes.length; j++) { + if (routeRequest.waysAttributes[i] === this._otherAttributes[j].key) { + finalAttributesKey.push(this._otherAttributes[j].key); + LOGGER.debug(this._otherAttributes[j].key + " added"); break; } } @@ -742,7 +782,7 @@ module.exports = class pgrSource extends Source { let askedProjection = routeRequest.start.projection; // start - start = new Point(response.waypoints[0].location[0], response.waypoints[0].location[1], this.topology.projection); + start = new Point(response.waypoints[0].location[0], response.waypoints[0].location[1], super.projection); if (!start.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of start in PGR response. "); } else { @@ -751,7 +791,7 @@ module.exports = class pgrSource extends Source { } // end - end = new Point(response.waypoints[response.waypoints.length-1].location[0], response.waypoints[response.waypoints.length-1].location[1], this.topology.projection); + end = new Point(response.waypoints[response.waypoints.length-1].location[0], response.waypoints[response.waypoints.length-1].location[1], super.projection); if (!end.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of end in PGR response. "); } else { @@ -779,7 +819,7 @@ module.exports = class pgrSource extends Source { let currentPgrRoute = response.routes[i]; // On commence par créer l'itinéraire avec les attributs obligatoires - routes[i] = new Route( new Line(currentPgrRoute.geometry, "geojson", this._topology.projection) ); + routes[i] = new Route( new Line(currentPgrRoute.geometry, "geojson", super.projection) ); if (!routes[i].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of geometry in PGR response. "); } else { @@ -798,7 +838,7 @@ module.exports = class pgrSource extends Source { let portionDuration = 0; let currentPgrRouteLeg = currentPgrRoute.legs[j]; - let legStart = new Point(response.waypoints[j].location[0], response.waypoints[j].location[1], this.topology.projection); + let legStart = new Point(response.waypoints[j].location[0], response.waypoints[j].location[1], super.projection); if (!legStart.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of leg start in OSRM response. "); } else { @@ -807,7 +847,7 @@ module.exports = class pgrSource extends Source { } - let legEnd = new Point(response.waypoints[j+1].location[0], response.waypoints[j+1].location[1], this.topology.projection); + let legEnd = new Point(response.waypoints[j+1].location[0], response.waypoints[j+1].location[1], super.projection); if (!legEnd.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of leg end in OSRM response. "); } else { @@ -940,7 +980,7 @@ module.exports = class pgrSource extends Source { newPortionGeomCoords.push(currentPgrRouteStep.geometry.coordinates); - steps[k] = new Step( new Line(currentPgrRouteStep.geometry, "geojson", this._topology.projection) ); + steps[k] = new Step( new Line(currentPgrRouteStep.geometry, "geojson", super.projection) ); if (!steps[k].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of step's geometry in PGR response. "); } else { @@ -983,7 +1023,7 @@ module.exports = class pgrSource extends Source { routes[i].duration = new Duration(Math.round(routeDuration * 10) / 10,"second"); currentPgrRoute.geometry.coordinates = newRouteGeomCoords; - routes[i].geometry = new Line(currentPgrRoute.geometry, "geojson", this._topology.projection); + routes[i].geometry = new Line(currentPgrRoute.geometry, "geojson", super.projection); if (!routes[i].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of step's geometry in PGR response. "); } else { @@ -1021,7 +1061,7 @@ module.exports = class pgrSource extends Source { } // Création d'un objet Point (utile plus tard). - point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, this.topology.projection); + point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, super.projection); let rawGeometry = JSON.parse(pgrResponse.rows[0].geometry); @@ -1037,7 +1077,7 @@ module.exports = class pgrSource extends Source { } // Création d'un objet Polygon à partir du GeoJSON reçu. - geometry = new Polygon(rawGeometry, "geojson", this._topology.projection); + geometry = new Polygon(rawGeometry, "geojson", super.projection); /* Envoi de la réponse au proxy. */ return new IsochroneResponse( diff --git a/src/js/sources/smartroutingSource.js b/src/js/sources/smartroutingSource.js index 1fd701f..3fc6d2d 100644 --- a/src/js/sources/smartroutingSource.js +++ b/src/js/sources/smartroutingSource.js @@ -42,13 +42,14 @@ module.exports = class smartroutingSource extends Source { constructor(sourceJsonObject) { // Constructeur parent - super(sourceJsonObject.id,sourceJsonObject.type, undefined); + super(sourceJsonObject.id, "pgr", sourceJsonObject.description, sourceJsonObject.projection, sourceJsonObject.bbox); // Stockage de la configuration this._configuration = sourceJsonObject; // Opérateur pour les requêtes http this._httpQuery = new httpQuery({prefixUrl: this._configuration.storage.url}); + } /** diff --git a/src/js/sources/source.js b/src/js/sources/source.js index 79c22ad..e115204 100644 --- a/src/js/sources/source.js +++ b/src/js/sources/source.js @@ -18,10 +18,9 @@ module.exports = class Source { * @description Constructeur de la classe source * @param {string} id - Identifiant de la source * @param {string} type - Type de la source - * @param {Topology} topology - Topologie dont dérive la source (classe fille de topology) * */ - constructor(id, type, topology) { + constructor(id, type, description, projection, bbox) { // Id d'une source. Il doit être unique. this._id = id; @@ -29,12 +28,18 @@ module.exports = class Source { // Type de la source this._type = type; + // Description de la source + this._description = description; + + // Projection de la source + this._projection = projection; + + // Emprise de la source + this._bbox = bbox; + // État de la connexion de la source this._connected = false; - // Topologie dont dérive la source - this._topology = topology; - // État de la source (même si connectée, elle peut être disfonctionnelle) // Peut être : "green" si la dernière requête a fonctionnée, "orange" si la source est connectée mais injoignable, "red" à l'initialisation ou si plus gros problème // Ajouter la gestion de ce paramètre dans chaque classe fille @@ -110,17 +115,6 @@ module.exports = class Source { this._state = st; } - /** - * - * @function - * @name get topology - * @description Récupérer la topologie de la source - * - */ - get topology () { - return this._topology; - } - /** * * @function diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index 7c7ac7b..b9f32f8 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -1,8 +1,6 @@ 'use strict'; const assert = require('assert').strict; -const storageManager = require('../utils/storageManager'); -const errorManager = require('../utils/errorManager'); const osrmSource = require('../sources/osrmSource'); const pgrSource = require('../sources/pgrSource'); const smartroutingSource = require('../sources/smartroutingSource'); @@ -21,7 +19,7 @@ module.exports = class sourceManager { * @description Constructeur de la classe sourceManager * */ - constructor(projectionManager) { + constructor(projectionManager, baseManager) { // Liste des ids des sources chargées par le manager this._loadedSourceId = new Array(); @@ -41,6 +39,9 @@ module.exports = class sourceManager { // Manager des projections this._projectionManager = projectionManager; + // Manager de bases de données (utiles pour certaines sources) + this._baseManager = baseManager; + // Correspondance entre les sources et les opérations possibles this._operationsByType = { "osrm": ["nearest", "route"], @@ -133,7 +134,7 @@ module.exports = class sourceManager { * */ - checkSourceDirectory(directory) { + async checkSourceDirectory(directory) { LOGGER.info("Vérification d'un dossier de sources..."); LOGGER.info("Nom du dossier: " + directory); @@ -204,7 +205,7 @@ module.exports = class sourceManager { * */ - checkSourceConfiguration(sourceJsonObject) { + async checkSourceConfiguration(sourceJsonObject) { LOGGER.info("Verification de la source..."); @@ -261,6 +262,43 @@ module.exports = class sourceManager { } + // Description + if (!sourceJsonObject.description) { + LOGGER.error("Mauvaise configuration: source.description absent"); + return false; + } else { + if (typeof(sourceJsonObject.description) !== "string") { + LOGGER.error("Mauvaise configuration: source.description n'est pas une chaine de caractère"); + return false; + } else { + LOGGER.debug("source.description présent"); + } + } + + // Projection + if (!sourceJsonObject.projection) { + LOGGER.error("Mauvaise configuration: source.projection absent"); + return false; + } else { + // Vérification de la projection + if (!this._projectionManager.isProjectionChecked(sourceJsonObject.projection)) { + LOGGER.error("La source indique une projection non disponible sur le service: " + sourceJsonObject.projection); + return false; + } + } + + // Bbox + if (!sourceJsonObject.bbox) { + LOGGER.error("Mauvaise configuration: source.bbox absent"); + return false; + } else { + // Vérification de la bbox + if (!this._projectionManager.checkBboxConfiguration(sourceJsonObject.bbox, sourceJsonObject.projection)) { + LOGGER.error("La source indique une bbox incorrecte: " + sourceJsonObject.bbox); + return false; + } + } + // Type if (!sourceJsonObject.type) { LOGGER.error("Mauvaise configuration: source.type absent"); @@ -283,7 +321,7 @@ module.exports = class sourceManager { if (sourceJsonObject.type === "osrm") { validation = this.checkSourceOsrm(sourceJsonObject); } else if (sourceJsonObject.type === "pgr") { - validation = this.checkSourcePgr(sourceJsonObject); + validation = await this.checkSourcePgr(sourceJsonObject); } else if (sourceJsonObject.type === "valhalla") { validation = this.checkSourceValhalla(sourceJsonObject); } else if (sourceJsonObject.type === "smartrouting") { @@ -302,31 +340,6 @@ module.exports = class sourceManager { } - // Projection - if (!sourceJsonObject.projection) { - LOGGER.error("Mauvaise configuration: source.projection absent"); - return false; - } else { - // Vérification de la projection - if (!this._projectionManager.isProjectionChecked(sourceJsonObject.projection)) { - LOGGER.error("La source indique une projection non disponible sur le service: " + topologyJsonDescription.projection); - return false; - } - } - - // Bbox - if (!sourceJsonObject.bbox) { - LOGGER.error("Mauvaise configuration: source.bbox absent"); - return false; - } else { - // Vérification de la bbox - if (!this._projectionManager.checkBboxConfiguration(sourceJsonObject.bbox, sourceJsonObject.projection)) { - LOGGER.error("La source indique une projection non disponible sur le service: " + topologyJsonDescription.projection); - return false; - } - } - - LOGGER.info("Fin de la verification de la source."); return true; @@ -338,7 +351,7 @@ module.exports = class sourceManager { * @name checkSourceOsrm * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source osrm. * @param {json} sourceJsonObject - Description JSON de la source - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @return {boolean} * */ @@ -356,52 +369,46 @@ module.exports = class sourceManager { // Storage if (!sourceJsonObject.storage) { - LOGGER.error("La ressource contient une source sans stockage."); + LOGGER.error("Mauvaise configuration : 'source.storage' absent"); return false; } else { - if (!storageManager.checkJsonStorage(sourceJsonObject.storage)) { - LOGGER.error("Stockage de la source incorrect."); + + LOGGER.debug("'source.storage' présent"); + + if (!sourceJsonObject.storage.file) { + LOGGER.error("Mauvaise configuration : 'source.storage.file' absent"); return false; } else { - // Normalement, il n'y a plus rien à faire car la fonction checkDuplicationSource() vérifie déjà que la source n'est pas dupliquée + LOGGER.debug("'source.storage.file' présent"); + // TODO : Vérifier l'existence et les droits de lecture } } // Cost if (!sourceJsonObject.cost) { - LOGGER.error("La ressource contient une source sans cout."); + LOGGER.error("Mauvaise configuration : 'source.cost' absent"); return false; } else { + + LOGGER.debug("'source.storage.cost' présent"); + // Profile if (!sourceJsonObject.cost.profile) { - LOGGER.error("La ressource contient une source sans profile."); + LOGGER.error("Mauvaise configuration : 'source.cost.profile' absent"); return false; } else { // rien à faire + LOGGER.debug("'source.cost.profile' présent"); } // Optimization if (!sourceJsonObject.cost.optimization) { - LOGGER.error("La ressource contient une source sans optimization."); + LOGGER.error("Mauvaise configuration : 'source.cost.optimization' absent"); return false; } else { // rien à faire + LOGGER.debug("'source.cost.optimization' présent"); } - // Compute - if (!sourceJsonObject.cost.compute) { - LOGGER.info("La ressource contient une source sans compute."); - } else { - if (!sourceJsonObject.cost.compute.storage) { - LOGGER.error("La ressource contient une source ayant un cout sans stockage."); - return false; - } else { - if (!storageManager.checkJsonStorage(sourceJsonObject.cost.compute.storage)) { - LOGGER.warn("La ressource contient une source ayant un stockage du cout incorrect."); - } else { - // rien à faire - } - } - //TODO: ajouter la vérification de configuration - } + } LOGGER.info("Fin de la verification de la source osrm."); @@ -414,11 +421,11 @@ module.exports = class sourceManager { * @name checkSourcePgr * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source pgr. * @param {json} sourceJsonObject - Description JSON de la source - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @return {boolean} * */ - checkSourcePgr(sourceJsonObject) { + async checkSourcePgr(sourceJsonObject) { LOGGER.info("Verification de la source pgr..."); @@ -432,57 +439,171 @@ module.exports = class sourceManager { // Storage if (!sourceJsonObject.storage) { - LOGGER.error("La ressource contient une source sans stockage."); + + LOGGER.error("Mauvaise configuration : 'source.storage' absent"); return false; + } else { - if (!storageManager.checkJsonStorage(sourceJsonObject.storage)) { - LOGGER.error("Stockage de la source incorrect."); + + LOGGER.debug("'source.storage' présent"); + + if (!sourceJsonObject.storage.base) { + + LOGGER.error("Mauvaise configuration : 'source.storage.base' absent"); return false; + } else { - // Normalement, il n'y a plus rien à faire car la fonction checkDuplicationSource() vérifie déjà que la source n'est pas dupliquée + + LOGGER.debug("'source.storage.base' présent"); + + if (!sourceJsonObject.storage.base.dbConfig) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.dbConfig' absent"); + return false; + } else { + LOGGER.debug("'source.storage.base.dbConfig' présent"); + + if (!(await this._baseManager.checkBaseConfiguration(sourceJsonObject.storage.base.dbConfig))) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.dbConfig' invalide"); + return false; + } else { + LOGGER.debug("'source.storage.base.dbConfig' valide"); + this._baseManager.saveCheckedBaseConfiguration(sourceJsonObject.storage.base.dbConfig); + } + + } + + if (!sourceJsonObject.storage.base.schema) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.schema' absent"); + return false; + } else { + + LOGGER.debug("'source.storage.base.schema' présent"); + + if (typeof(sourceJsonObject.storage.base.schema) !== "string") { + LOGGER.error("Mauvaise configuration : 'source.storage.base.schema' n'est pas une chaine de caractères"); + return false; + } + + } + + if (!sourceJsonObject.storage.base.attributes) { + // Cet élément n'est pas obligatoire pour laisser plus de liberté + LOGGER.info("Configuration : 'source.storage.base.attributes' absent"); + } else { + + LOGGER.debug("'source.storage.base.attributes' présent"); + + if (!Array.isArray(sourceJsonObject.storage.base.attributes)) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes' n'est pas un tableau"); + return false; + } else { + LOGGER.debug("'source.storage.base.attributes' est un tableau"); + } + + if (sourceJsonObject.storage.base.attributes.length === 0) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes' est un tableau vide"); + return false; + } else { + LOGGER.debug("'source.storage.base.attributes' est un tableau non vide"); + } + + for (let j = 0; j < sourceJsonObject.storage.base.attributes.length; j++) { + + if (sourceJsonObject.storage.base.attributes[j].key) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].key' est absent"); + return false; + } + + if (sourceJsonObject.storage.base.attributes[j].column) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].column' est absent"); + return false; + } + + if (sourceJsonObject.storage.base.attributes[j].default) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].default' est absent"); + return false; + } else { + if (sourceJsonObject.storage.base.attributes[j].default !== "true" && sourceJsonObject.storage.base.attributes[j].default !== "false" ) { + LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].default' doit être 'true' or 'false' (string)"); + return false; + } + } + + } + + } + } + } - // Cost - if (!sourceJsonObject.cost) { - LOGGER.error("La ressource contient une source sans cout."); + // Coûts + if (!sourceJsonObject.costs) { + LOGGER.error("Mauvaise configuration : 'source.costs' absent"); return false; } else { - // Profile - if (!sourceJsonObject.cost.profile) { - LOGGER.error("La ressource contient une source sans profile."); + + LOGGER.debug("'source.costs' présent"); + + if (!Array.isArray(sourceJsonObject.costs)) { + LOGGER.error("Mauvaise configuration : 'source.costs' n'est pas un tableau"); return false; } else { - // rien à faire + LOGGER.debug("'source.costs' est un tableau"); } - // Optimization - if (!sourceJsonObject.cost.optimization) { - LOGGER.error("La ressource contient une source sans optimization."); + + if (sourceJsonObject.costs.length === 0) { + LOGGER.error("Mauvaise configuration : 'source.costs' est un tableau vide"); return false; } else { - // rien à faire + LOGGER.debug("'source.costs' n'est pas un tableau vide"); } - // Compute - if (!sourceJsonObject.cost.compute) { - LOGGER.info("La ressource contient une source sans compute."); - } else { - if (!sourceJsonObject.cost.compute.storage) { - LOGGER.error("La ressource contient une source ayant un cout sans stockage."); + + for (let i = 0; i < sourceJsonObject.costs.length; i++) { + + if (!sourceJsonObject.costs[i].profile) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].profile' absent"); return false; } else { - if (!storageManager.checkJsonStorage(sourceJsonObject.cost.compute.storage)) { - LOGGER.error("La ressource contient une source ayant un stockage du cout incorrect."); - return false; - } else { - // rien à faire - } + LOGGER.debug("'source.costs["+i+"].profile' présent"); } - // TODO: ajouter la vérification de configuration + + if (!sourceJsonObject.costs[i].optimization) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].optimization' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].optimization' présent"); + } + + if (!sourceJsonObject.costs[i].costType) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].costType' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].costType' présent"); + } + + if (!sourceJsonObject.costs[i].costColumn) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].costColumn' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].costColumn' présent"); + } + + if (!sourceJsonObject.costs[i].rcostColumn) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].rcostColumn' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].rcostColumn' présent"); + } + } + } + LOGGER.info("Fin de la verification de la source pgr."); return true; + } /** @@ -491,29 +612,34 @@ module.exports = class sourceManager { * @name checkSourceSmartrouting * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source smartrouting. * @param {json} sourceJsonObject - Description JSON de la source - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @return {boolean} * */ - checkSourceSmartrouting(sourceJsonObject) { + checkSourceSmartrouting(sourceJsonObject) { LOGGER.info("Verification de la source smartrouting..."); // Storage if (!sourceJsonObject.storage) { - LOGGER.error("La ressource contient une source sans stockage."); + LOGGER.error("Mauvaise configuration : 'source.storage' absent"); return false; } else { - if (!storageManager.checkJsonStorage(sourceJsonObject.storage)) { - LOGGER.error("Stockage de la source incorrect."); + + LOGGER.debug("'source.storage' présent"); + + if (!sourceJsonObject.storage.url) { + LOGGER.error("Mauvaise configuration : 'source.storage.url' absent"); return false; } else { - // Normalement, il n'y a plus rien à faire car la fonction checkDuplicationSource() vérifie déjà que la source n'est pas dupliquée + LOGGER.debug("'source.storage.url' présent"); + // TODO: vérifier la forme de l'URL avec une regex } } LOGGER.info("Fin de la verification de la source smartrouting."); return true; + } /** @@ -522,7 +648,7 @@ module.exports = class sourceManager { * @name checkSourceValhalla * @description Fonction utilisée pour vérifier le contenu d'un fichier de description d'une source valhalla. * @param {json} sourceJsonObject - Description JSON de la source - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur + * @return {boolean} * */ @@ -532,19 +658,96 @@ module.exports = class sourceManager { // Storage if (!sourceJsonObject.storage) { - LOGGER.error("La ressource contient une source sans stockage."); + + LOGGER.error("Mauvaise configuration : 'source.storage' absent"); return false; + } else { - if (!storageManager.checkJsonStorage(sourceJsonObject.storage)) { - LOGGER.error("Stockage de la source incorrect."); + + LOGGER.debug("'source.storage' présent"); + + if (!sourceJsonObject.storage.tar) { + LOGGER.error("Mauvaise configuration : 'source.storage.tar' absent"); return false; } else { - // Normalement, il n'y a plus rien à faire car la fonction checkDuplicationSource() vérifie déjà que la source n'est pas dupliquée + LOGGER.debug("'source.storage.tar' présent"); + } + + if (!sourceJsonObject.storage.dir) { + LOGGER.error("Mauvaise configuration : 'source.storage.dir' absent"); + return false; + } else { + LOGGER.debug("'source.storage.dir' présent"); + } + + if (!sourceJsonObject.storage.config) { + LOGGER.error("Mauvaise configuration : 'source.storage.config' absent"); + return false; + } else { + LOGGER.debug("'source.storage.config' présent"); + } + + } + + // Coûts + if (!sourceJsonObject.costs) { + LOGGER.error("Mauvaise configuration : 'source.costs' absent"); + return false; + } else { + + LOGGER.debug("'source.costs' présent"); + + if (!Array.isArray(sourceJsonObject.costs)) { + LOGGER.error("Mauvaise configuration : 'source.costs' n'est pas un tableau"); + return false; + } else { + LOGGER.debug("'source.costs' est un tableau"); + } + + if (sourceJsonObject.costs.length === 0) { + LOGGER.error("Mauvaise configuration : 'source.costs' est un tableau vide"); + return false; + } else { + LOGGER.debug("'source.costs' n'est pas un tableau vide"); + } + + for (let i = 0; i < sourceJsonObject.costs.length; i++) { + + if (!sourceJsonObject.costs[i].profile) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].profile' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].profile' présent"); + } + + if (!sourceJsonObject.costs[i].optimization) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].optimization' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].optimization' présent"); + } + + if (!sourceJsonObject.costs[i].costType) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].costType' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].costType' présent"); + } + + if (!sourceJsonObject.costs[i].costing) { + LOGGER.error("Mauvaise configuration : 'source.costs["+i+"].costing' absent"); + return false; + } else { + LOGGER.debug("'source.costs["+i+"].costing' présent"); + } + } + } LOGGER.info("Fin de la verification de la source smartrouting."); return true; + } /** @@ -704,20 +907,31 @@ module.exports = class sourceManager { } if (sourceJsonObject.type === "osrm") { - source = new osrmSource(sourceJsonObject, topology); + source = new osrmSource(sourceJsonObject); } else if (sourceJsonObject.type === "pgr") { - source = new pgrSource(sourceJsonObject, topology); + + // Création de la base associée + let base = {}; + if (!this._baseManager.loadBaseConfiguration(sourceJsonObject.storage.base.dbConfig)) { + LOGGER.error("Impossible de charger la base configurée dans " + sourceJsonObject.storage.base.dbConfig); + return false; + } else { + base = this._baseManager.getBase(sourceJsonObject.storage.base.dbConfig); + } + // Création de la source + source = new pgrSource(sourceJsonObject, base); + } else if (sourceJsonObject.type === "smartrouting") { - // smartrouting n'utilise pas la topologie définie dans la conf source = new smartroutingSource(sourceJsonObject); } else if (sourceJsonObject.type === "valhalla") { - source = new valhallaSource(sourceJsonObject, topology); + source = new valhallaSource(sourceJsonObject); } else { - // On va voir si c'est un autre type. LOGGER.error("Le type de la source est inconnu"); return false; } + // On sauvegarde la source et certains éléments pour le manager afin de pouvoir réutiliser ces infomations plus tard + // Notamment dans la gestion (ajout/suppression/modification) de sources durant la vie du service this._loadedSourceId.push(sourceJsonObject.id); this._loadedSourceConfiguration[sourceJsonObject.id] = sourceJsonObject; this._source[sourceJsonObject.id] = source; diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js index dd86535..fa6c83d 100644 --- a/src/js/sources/valhallaSource.js +++ b/src/js/sources/valhallaSource.js @@ -22,7 +22,7 @@ const LOGGER = log4js.getLogger("VALHALLASOURCE"); * * @class * @name valhallaSource -* @description Classe modélisant une source pgRouting. +* @description Classe modélisant une source Valhalla. * */ module.exports = class valhallaSource extends Source { @@ -32,17 +32,27 @@ module.exports = class valhallaSource extends Source { * @name constructor * @description Constructeur de la classe valhallaSource * @param{json} sourceJsonObject - Description de la source - * @param{topology} topology - Instance de la classe Topology * */ - constructor(sourceJsonObject, topology) { + constructor(sourceJsonObject) { // Constructeur parent - super(sourceJsonObject.id, "valhalla", topology); + super(sourceJsonObject.id, "valhalla", sourceJsonObject.description, sourceJsonObject.projection, sourceJsonObject.bbox); // Stockage de la configuration this._configuration = sourceJsonObject; + // Gestions des coûts disponibles + this._costs = {}; + + // Initialisation des coûts + for (let i = 0; i < sourceJsonObject.costs.length; i++) { + Object.defineProperty(this._costs, sourceJsonObject.costs[i].profile, { value: new Object(), configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile], sourceJsonObject.costs[i].optimization, { value: new Object(), configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].optimization], "costing", { value: sourceJsonObject.costs[i].costing, configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].costType], "costing", { value: sourceJsonObject.costs[i].costing, configurable: true, enumerable: true, writable: true }); + } + } /** @@ -119,15 +129,15 @@ module.exports = class valhallaSource extends Source { // Coordonnées // start - coordinatesTable.push(request.start.getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.start.getCoordinatesIn(super.projection)); // intermediates if (request.intermediates.length !== 0) { for (let i = 0; i < request.intermediates.length; i++) { - coordinatesTable.push(request.intermediates[i].getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.intermediates[i].getCoordinatesIn(super.projection)); } } // end - coordinatesTable.push(request.end.getCoordinatesIn(this.topology.projection)); + coordinatesTable.push(request.end.getCoordinatesIn(super.projection)); LOGGER.debug("coordinates:"); LOGGER.debug(coordinatesTable); @@ -169,7 +179,7 @@ module.exports = class valhallaSource extends Source { locationsString = locationsString.slice(0, -1); locationsString += "]"; - const costingString = `"costing":"${this._configuration.cost.compute.configuration.costing}"`; + const costingString = `"costing":"${this._costs[request.profile][request.optimization].costing}"`; // Permet de grandement se simplifier le parsing !! const optionsString = `"directions_options":{"format":"osrm"}`; const commandString = `valhalla_service ${this._configuration.storage.config} route '{${locationsString},${costingString},${optionsString}}' `; @@ -258,7 +268,7 @@ module.exports = class valhallaSource extends Source { } const locationsString = `"locations":[{"lat":${request.point.lat},"lon":${request.point.lon}}]`; - const costingString = `"costing":"${this._configuration.cost.compute.configuration.costing}"`; + const costingString = `"costing":"${this._costs[request.profile][request.costType].costing}"`; const contoursString = `"contours":[{"${request.costType}":${costValue}}]`; const reverseString = `"reverse":${reverse}`; const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${contoursString},${reverseString}}' `; @@ -372,10 +382,10 @@ module.exports = class valhallaSource extends Source { let askedProjection = routeRequest.start.projection; LOGGER.debug("asked projection: " + askedProjection); - LOGGER.debug("topology projection: " + this.topology.projection); + LOGGER.debug("source projection: " + super.projection); // start - start = new Point(valhallaResponse.waypoints[0].location[0], valhallaResponse.waypoints[0].location[1], this.topology.projection); + start = new Point(valhallaResponse.waypoints[0].location[0], valhallaResponse.waypoints[0].location[1], super.projection); if (!start.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of start in OSRM response. "); } else { @@ -384,7 +394,7 @@ module.exports = class valhallaSource extends Source { } // end - end = new Point(valhallaResponse.waypoints[valhallaResponse.waypoints.length-1].location[0], valhallaResponse.waypoints[valhallaResponse.waypoints.length-1].location[1], this.topology.projection); + end = new Point(valhallaResponse.waypoints[valhallaResponse.waypoints.length-1].location[0], valhallaResponse.waypoints[valhallaResponse.waypoints.length-1].location[1], super.projection); if (!end.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of end in OSRM response. "); } else { @@ -411,7 +421,7 @@ module.exports = class valhallaSource extends Source { let currentOsrmRoute = valhallaResponse.routes[i]; // On commence par créer l'itinéraire avec les attributs obligatoires - routes[i] = new Route( new Line(currentOsrmRoute.geometry, "polyline", this._topology.projection, 6) ); + routes[i] = new Route( new Line(currentOsrmRoute.geometry, "polyline", super.projection, 6) ); if (!routes[i].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of geometry in OSRM response. "); } else { @@ -437,7 +447,7 @@ module.exports = class valhallaSource extends Source { let currentOsrmRouteLeg = currentOsrmRoute.legs[j]; - let legStart = new Point(valhallaResponse.waypoints[j].location[0], valhallaResponse.waypoints[j].location[1], this.topology.projection); + let legStart = new Point(valhallaResponse.waypoints[j].location[0], valhallaResponse.waypoints[j].location[1], super.projection); if (!legStart.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of leg start in OSRM response. "); } else { @@ -445,7 +455,7 @@ module.exports = class valhallaSource extends Source { LOGGER.debug(legStart); } - let legEnd = new Point(valhallaResponse.waypoints[j+1].location[0], valhallaResponse.waypoints[j+1].location[1], this.topology.projection); + let legEnd = new Point(valhallaResponse.waypoints[j+1].location[0], valhallaResponse.waypoints[j+1].location[1], super.projection); if (!legEnd.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of leg end in OSRM response. "); } else { @@ -468,7 +478,7 @@ module.exports = class valhallaSource extends Source { LOGGER.debug("Step number " + k + " of portion number " + j + " for route number " + i); let currentOsrmRouteStep = currentOsrmRouteLeg.steps[k]; - steps[k] = new Step( new Line(currentOsrmRouteStep.geometry, "polyline", this._topology.projection, 6) ); + steps[k] = new Step( new Line(currentOsrmRouteStep.geometry, "polyline", super.projection, 6) ); if (!steps[k].geometry.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of step's geometry in OSRM response. "); } else { @@ -545,7 +555,7 @@ module.exports = class valhallaSource extends Source { } // Création d'un objet Point (utile plus tard). - point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, this.topology.projection); + point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, super.projection); let rawGeometry = valhallaResponse.features[0].geometry; @@ -561,7 +571,7 @@ module.exports = class valhallaSource extends Source { } // Création d'un objet Polygon à partir du GeoJSON reçu. - geometry = new Polygon(rawGeometry, "geojson", this._topology.projection); + geometry = new Polygon(rawGeometry, "geojson", super.projection); /* Envoi de la réponse au proxy. */ return new IsochroneResponse( diff --git a/src/js/topology/dbTopology.js b/src/js/topology/dbTopology.js deleted file mode 100644 index 45217fd..0000000 --- a/src/js/topology/dbTopology.js +++ /dev/null @@ -1,137 +0,0 @@ -'use strict'; - -const Topology = require('./topology'); - -/** -* -* @class -* @name dbTopology -* @description Classe modélisant une topologie se basant sur une base de données. -* -*/ -module.exports = class dbTopology extends Topology { - - /** - * - * @function - * @name constructor - * @description Constructeur de la classe dbTopology - * @param{string} id - Id de la topologie - * @param{string} description - Description de la topologie - * @param{string} projection - Projection de la topologie - * @param{string} bbox - Bbox de la topologie - * @param{object} base - Instance de la classe Base - * @param{string} schema - Nom de la schema contenant la topologie - * @param{table} defaultAttributes - Tableau d'objets {key: 'test', column:'test_column', default: 'true'} - * @param{table} otherAttributes - Tableau d'objets {key: 'test', column:'test_column', default: 'false'} - * - */ - constructor(id, description, projection, bbox, base, schema, defaultAttributes, otherAttributes) { - - // ID de la topologie - super(id, "db", description, projection, bbox); - - // Référence à la base de données - this._base = base; - - // Schema contenant la topologie - this._schema = schema; - - // stockage des attributs par défaut - this._defaultAttributes = defaultAttributes; - - // stockage des attributs dans un tableau - this._defaultAttributesTable = new Array(); - this._defaultAttributesKeyTable = new Array(); - for (let i = 0; i < this._defaultAttributes.length; i++) { - this._defaultAttributesTable.push("'" + this._defaultAttributes[i].column + "'"); - this._defaultAttributesKeyTable.push(this._defaultAttributes[i].key); - } - - // stockage des attributs par défaut en une chaîne de caractères - this._defaultAttributesString = this._defaultAttributesTable.join(","); - - // stockage des attributs restants et disponibles - this._otherAttributes = otherAttributes; - - } - - /** - * - * @function - * @name get base - * @description Récupérer la base - * - */ - get base () { - return this._base; - } - - /** - * - * @function - * @name get schema - * @description Récupérer le schema - * - */ - get schema () { - return this._schema; - } - - /** - * - * @function - * @name get defaultAttributes - * @description Récupérer les attributs (colonne) par défaut de la topologie - * - */ - get defaultAttributes () { - return this._defaultAttributes; - } - - /** - * - * @function - * @name get defaultAttributesString - * @description Récupérer les attributs (colonne) par défaut de la topologie en une chaîne de caractères - * - */ - get defaultAttributesString () { - return this._defaultAttributesString; - } - - /** - * - * @function - * @name get defaultAttributesTable - * @description Récupérer les attributs (colonne) par défaut de la topologie en tableau - * - */ - get defaultAttributesTable () { - return this._defaultAttributesTable; - } - - /** - * - * @function - * @name get defaultAttributesKeyTable - * @description Récupérer les attributs (clé) par défaut de la topologie en tableau - * - */ - get defaultAttributesKeyTable () { - return this._defaultAttributesKeyTable; - } - - /** - * - * @function - * @name get otherAttributes - * @description Récupérer les attributs facultatifs de la topologie - * - */ - get otherAttributes () { - return this._otherAttributes; - } - - -} diff --git a/src/js/topology/osmTopology.js b/src/js/topology/osmTopology.js deleted file mode 100644 index 7604d78..0000000 --- a/src/js/topology/osmTopology.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -const Topology = require('./topology'); - -/** -* -* @class -* @name dbTopology -* @description Classe modélisant une topologie se basant sur un fichier osm. -* -*/ -module.exports = class osmTopology extends Topology { - - /** - * - * @function - * @name constructor - * @description Constructeur de la classe dbTopology - * @param{string} id - Id de la topologie - * @param{string} description - Description de la topologie - * @param{string} projection - Projection de la topologie - * @param{string} bbox - Bbox de la topologie - * @param{string} file - Nom du fichier osm - * - */ - constructor(id, description, projection, bbox, file) { - - // ID de la topologie - super(id, "osm", description, projection, bbox); - - // Nom du fichier osm - this._file = file; - - } - - /** - * - * @function - * @name get file - * @description Récupérer le nom du fichier osm - * - */ - get file () { - return this._file; - } - -} diff --git a/src/js/topology/topology.js b/src/js/topology/topology.js deleted file mode 100644 index 1a6c934..0000000 --- a/src/js/topology/topology.js +++ /dev/null @@ -1,99 +0,0 @@ -'use strict'; - -/** -* -* @class -* @name Topology -* @description Classe modélisant une topologie. -* -*/ -module.exports = class Topology { - - /** - * - * @function - * @name constructor - * @description Constructeur de la classe Topology - * @param{string} id - Id de la topologie - * @param{string} type - Type de la topologie - * @param{string} description - Description de la topologie - * @param{string} projection - Projection de la topologie - * @param{string} bbox - Bbox de la topologie - * - */ - constructor(id, type, description, projection, bbox) { - - // ID de la topologie - this._id = id; - - // type - this._type = type; - - // Description - this._description = description; - - // Projection - this._projection = projection; - - // Bbox - this._bbox = bbox; - - } - - /** - * - * @function - * @name get id - * @description Récupérer l'id - * - */ - get id () { - return this._id; - } - - /** - * - * @function - * @name get type - * @description Récupérer le type - * - */ - get type () { - return this._type; - } - - /** - * - * @function - * @name get description - * @description Récupérer la description - * - */ - get description () { - return this._description; - } - - /** - * - * @function - * @name get projection - * @description Récupérer la projection - * - */ - get projection () { - return this._projection; - } - - /** - * - * @function - * @name get bbox - * @description Récupérer la bbox - * - */ - get bbox () { - return this._bbox; - } - - -} diff --git a/src/js/topology/topologyManager.js b/src/js/topology/topologyManager.js deleted file mode 100644 index c3ba6b9..0000000 --- a/src/js/topology/topologyManager.js +++ /dev/null @@ -1,515 +0,0 @@ -'use strict'; - -const log4js = require('log4js'); -const storageManager = require('../utils/storageManager'); -const DbTopology = require('./dbTopology'); -const OsmTopology = require('./osmTopology'); -const assert = require('assert'); - -// Création du LOGGER -const LOGGER = log4js.getLogger("TOPOLOGYMANAGER"); - -/** -* -* @class -* @name topologyManager -* @description Classe modélisant le manager des topologies. -* -*/ -module.exports = class topologyManager { - - /** - * - * @function - * @name constructor - * @description Constructeur de la classe topologyManager - * @param{BaseManager} baseManager - Manager de bases - * - */ - constructor(baseManager, projectionManager) { - - // Liste des descriptions de topologies chargées par le manager - this._loadedTopologyId = new Array(); - - // Liste des descriptions de topologies vérifiées et validées par le manager - this._checkedTopologyId = new Array(); - - // Descriptions des topologies chargées par le manager - this._loadedTopologyConfiguration = {}; - - // Descriptions des topologies vérifiées par le manager - this._checkedTopologyConfiguration = {}; - - // Le catalogue des topologies créées par le manager - this._topologyCatalog = {}; - - // Manager de bases - this._baseManager = baseManager; - - // Manager de projections - this._projectionManager = projectionManager; - - } - - /** - * - * @function - * @name get topologyCatalog - * @description Récupérer le catalogue des topologies - * - */ - get topologyCatalog() { - return this._topologyCatalog; - } - - /** - * - * @function - * @name getTopology - * @description Récupérer une topologie via son id - * @param {string} id - Id de la topologie - * - */ - getTopology(id) { - if (this._topologyCatalog[id]) { - return this._topologyCatalog[id]; - } else { - return {}; - } - - } - - /** - * - * @function - * @name checkTopologyConfiguration - * @description Vérification de la description d'une topologie - * @param{json} topologyJsonDescription - JSON décrivant une topologie - * - */ - async checkTopologyConfiguration(topologyJsonDescription) { - - // Id de la topologie - if (!topologyJsonDescription.id) { - LOGGER.error("La topologie ne contient pas d'id."); - return false; - } else { - - // On vérifie que l'id n'est pas déjà chargés. - if (this._loadedTopologyId.length !== 0) { - - for (let i = 0; i < this._loadedTopologyId.length; i++ ) { - if (this._loadedTopologyId[i] === topologyJsonDescription.id) { - - LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est deja chargée."); - // On vérifie que la topology décrite et celle déjà identifiée soient exactement les mêmes - if (this.checkDuplicationLoadedTopology(topologyJsonDescription)) { - LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est identique à la topologie deja identifiee."); - return true; - } else { - LOGGER.error("La topologie contenant l'id " + topologyJsonDescription.id + " n'est pas identique à la topologie deja identifiee."); - return false; - } - - } - } - } - - // On vérifie que l'id n'est pas déjà vérifiés. - if (this._checkedTopologyId.length !== 0) { - - for (let i = 0; i < this._checkedTopologyId.length; i++ ) { - if (this._checkedTopologyId[i] === topologyJsonDescription.id) { - - LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est deja vérifiée."); - // On vérifie que la topology décrite et celle déjà identifiée soient exactement les mêmes - if (this.checkDuplicationCheckedTopology(topologyJsonDescription)) { - LOGGER.info("La topologie contenant l'id " + topologyJsonDescription.id + " est identique à la topologie deja identifiee."); - return true; - } else { - LOGGER.error("La topologie contenant l'id " + topologyJsonDescription.id + " n'est pas identique à la topologie deja identifiee."); - return false; - } - - } - } - } - } - - // Description de la topologie - if (!topologyJsonDescription.description) { - LOGGER.error("La ressource ne contient pas de description de la topologie."); - return false; - } else { - // rien à faire - } - - // Projection de la topologie - if (!topologyJsonDescription.projection) { - LOGGER.error("La ressource ne contient pas d'information sur la projection de la topologie.") - return false; - } else { - // Vérification de la projection - if (!this._projectionManager.isProjectionChecked(topologyJsonDescription.projection)) { - LOGGER.error("La topologie indique une projection non disponible sur le service: " + topologyJsonDescription.projection); - return false; - } - } - - // Bbox de la topologie - if (!topologyJsonDescription.bbox) { - LOGGER.error("La ressource ne contient pas d'information sur la bbox de la topologie.") - return false; - } else { - // TODO: vérifier la bbox - } - - if (!topologyJsonDescription.type) { - LOGGER.error("La ressource ne contient pas de type de la topologie."); - return false; - } else { - - if (topologyJsonDescription.type === "db") { - if (!(await this.checkDbTopology(topologyJsonDescription))) { - LOGGER.error("La topologie db est incorrecte."); - return false; - } - } else if (topologyJsonDescription.type === "osm") { - if (!this.checkOsmTopology(topologyJsonDescription)) { - LOGGER.error("La topologie osm est incorrecte."); - return false; - } - } else { - LOGGER.error("La ressource contient un type inconnu pour la topologie."); - return false; - } - - } - - return true; - - } - - /** - * - * @function - * @name checkOsmTopology - * @description Vérification de la description d'une topologie OSM - * @param{json} topologyJsonDescription - JSON décrivant une topologie - * - */ - checkOsmTopology(topologyJsonDescription) { - - // Stockage de la topologie - if (!topologyJsonDescription.storage) { - LOGGER.info("La ressource ne contient pas d'information sur le stockage du fichier de generation de la topologie."); - } else { - if (!storageManager.checkJsonStorage(topologyJsonDescription.storage)) { - LOGGER.warn("Stockage de la topologie incorrect."); - } else { - // rien à faire - } - } - - return true; - - } - - /** - * - * @function - * @name checkDbTopology - * @description Vérification de la description d'une topologie issue d'une base de données - * @param{json} topologyJsonDescription - JSON décrivant une topologie - * - */ - async checkDbTopology(topologyJsonDescription) { - - // Stockage de la topologie - if (!topologyJsonDescription.storage) { - LOGGER.error("La ressource ne contient pas d'information sur le stockage du fichier de generation de la topologie."); - return false; - } else { - // on continue - } - - if (!topologyJsonDescription.storage.base) { - LOGGER.error("La ressource ne contient pas de parametre 'topology.storage.base'."); - return false; - } - - // dbConfig - if (!topologyJsonDescription.storage.base.dbConfig) { - LOGGER.error("La ressource ne contient pas de parametre 'topology.storage.dbConfig'."); - return false; - } else { - if (!(await this._baseManager.checkBaseConfiguration(topologyJsonDescription.storage.base.dbConfig))) { - LOGGER.error("La ressource contient un parametre 'topology.storage.dbConfig' incorrect."); - return false; - } else { - this._baseManager.saveCheckedBaseConfiguration(topologyJsonDescription.storage.base.dbConfig); - } - } - - // schema - if (!topologyJsonDescription.storage.base.schema) { - LOGGER.error("La ressource ne contient pas de parametre 'topology.storage.base.schema'."); - return false; - } else { - // TODO: vérification que ce n'est pas du code injecté - } - - // Attributs - if (topologyJsonDescription.storage.base.attributes) { - - // on vérifie que c'est un tableau - if (!Array.isArray(topologyJsonDescription.storage.base.attributes)) { - LOGGER.error("Le parametre resource.topology.attributes n'est pas un tableau."); - return false; - } - - // que le tableau n'est pas vide - if (topologyJsonDescription.storage.base.attributes.length === 0) { - LOGGER.error("Le parametre resource.topology.attributes est un tableau vide."); - return false; - } - - // on va vérifier que chaque attribut est complet et unique dans sa description - let attributesKeyTable = new Array(); - let attributesColumnTable = new Array(); - - for (let i = 0; i < topologyJsonDescription.storage.base.attributes.length; i++) { - let curAttribute = topologyJsonDescription.storage.base.attributes[i]; - - - if (!curAttribute.key) { - LOGGER.error("La description de l'attribut est incomplete: key"); - return false; - } else { - - if (attributesKeyTable.length !== 0) { - for (let j = 0; j < attributesKeyTable.length; j++) { - if (curAttribute.key === attributesKeyTable[j]) { - LOGGER.error("La description de l'attribut indique une cle deja utilisee."); - return false; - } - } - } - - } - - if (!curAttribute.column) { - LOGGER.error("La description de l'attribut est incomplete: column"); - return false; - } else { - - if (attributesColumnTable.length !== 0) { - for (let j = 0; j < attributesColumnTable.length; j++) { - if (curAttribute.column === attributesColumnTable[j]) { - LOGGER.error("La description de l'attribut indique une colonne deja utilisee."); - return false; - } - } - } - - // TODO: vérification que ce n'est pas du code injecté - - } - - if (!curAttribute.default) { - LOGGER.error("La description de l'attribut est incomplete: default"); - return false; - } else { - - if (curAttribute.default !== "true" && curAttribute.default !== "false") { - LOGGER.error("La description de l'attribut a un parametre 'default' incorrect."); - return false; - } - - } - - attributesKeyTable.push(curAttribute.key); - attributesColumnTable.push(curAttribute.column); - - } - } else { - // rien à faire - } - - return true; - - } - - /** - * - * @function - * @name checkDuplicationLoadedTopology - * @description Fonction utilisée pour vérifier que le contenu d'un fichier de description d'une source est bien le même qu'un autre. - * @param {json} topologyJsonDescription - Description JSON de la topologie - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur - * - */ - - checkDuplicationLoadedTopology(topologyJsonDescription) { - - LOGGER.info("Comparaison des deux topologies identifiees et devant etre identiques..."); - - // On récupère la description de la topologie faisant office de référence car lue la première. - let referenceTopology = this._loadedTopologyConfiguration[topologyJsonDescription.id]; - - // On compare les deux objets - try { - assert.deepStrictEqual(topologyJsonDescription, referenceTopology); - } catch (err) { - LOGGER.error("Les deux topologies ne sont pas identiques."); - LOGGER.error(err); - return false; - } - - LOGGER.info("Les deux topologies sont identiques."); - return true; - - } - - /** - * - * @function - * @name checkDuplicationCheckedTopology - * @description Fonction utilisée pour vérifier que le contenu d'un fichier de description d'une source est bien le même qu'un autre. - * @param {json} topologyJsonDescription - Description JSON de la topologie - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur - * - */ - - checkDuplicationCheckedTopology(topologyJsonDescription) { - - LOGGER.info("Comparaison des deux topologies identifiees et devant etre identiques..."); - - // On récupère la description de la topologie faisant office de référence car lue la première. - let referenceTopology = this._checkedTopologyConfiguration[topologyJsonDescription.id]; - - // On compare les deux objets - try { - assert.deepStrictEqual(topologyJsonDescription, referenceTopology); - } catch (err) { - LOGGER.error("Les deux topologies ne sont pas identiques."); - LOGGER.error(err); - return false; - } - - LOGGER.info("Les deux topologies sont identiques."); - return true; - - } - - /** - * - * @function - * @name saveCheckedTopology - * @description Sauvegarder l'id de la topology vérifié - * @param {object} configuration - Id de la topology que l'on veut sauvegarder - * - */ - saveCheckedTopology(configuration) { - - this._checkedTopologyId.push(configuration.id); - this._checkedTopologyConfiguration[configuration.id] = configuration; - - } - - /** - * - * @function - * @name flushCheckedTopology - * @description Vider la liste des topology déjà vérifiées - * - */ - flushCheckedTopology() { - - this._checkedTopologyId = new Array(); - this._checkedTopologyConfiguration = {}; - - } - - /** - * - * @function - * @name loadTopologyConfiguration - * @description Fonction utilisée pour créer une source. - * @param {json} topologyJsonObject - Description JSON de la topologie - * @return {Topology} Topologie créée - Instance d'une classe fille de Topology - * - */ - - loadTopologyConfiguration(topologyJsonObject) { - - LOGGER.info("Creation de la topologie: " + topologyJsonObject.id); - - let topology; - - // on vérifie d'abord que la topologie n'a pas déjà été créée - if (this._topologyCatalog[topologyJsonObject.id]) { - LOGGER.info("La topologie " + topologyJsonObject.id + " existe déjà"); - return true; - } else { - // la topologie n'existe pas, on continue - } - - if (topologyJsonObject.type === "osm") { - - let osmFile = ""; - if (topologyJsonObject.storage) { - osmFile = topologyJsonObject.storage.file; - } else { - // ce n'est pas obligatoire - } - - topology = new OsmTopology(topologyJsonObject.id, topologyJsonObject.description, - topologyJsonObject.projection, topologyJsonObject.bbox, osmFile); - - } else if (topologyJsonObject.type === "db") { - - // récupération de la base - let base = {}; - if (!this._baseManager.loadBaseConfiguration(topologyJsonObject.storage.base.dbConfig)) { - LOGGER.error("Impossible de charger la base configurée dans " + topologyJsonObject.storage.base.dbConfig); - return false; - } else { - base = this._baseManager.getBase(topologyJsonObject.storage.base.dbConfig); - } - - // création des tableaux d'attributs - let defaultAttributes = new Array(); - let otherAttributes = new Array(); - for (let i = 0; i < topologyJsonObject.storage.base.attributes.length; i++) { - let curAttribute = topologyJsonObject.storage.base.attributes[i]; - if (curAttribute.default === "true") { - defaultAttributes.push(curAttribute); - } else if (curAttribute.default === "false") { - otherAttributes.push(curAttribute); - } else { - // cela ne doit pas arriver - } - } - - // création de la topologie - topology = new DbTopology(topologyJsonObject.id, topologyJsonObject.description, - topologyJsonObject.projection, topologyJsonObject.bbox, base, topologyJsonObject.storage.base.schema, - defaultAttributes, otherAttributes); - - } else { - // On va voir si c'est un autre type. - LOGGER.error("Type de la topology inconnu"); - return false; - } - - // on sauvegarde l'id de la topologie pour savoir qu'elle a déjà été vérifiée et que sa description est valide - this._loadedTopologyId.push(topologyJsonObject.id); - this._loadedTopologyConfiguration[topologyJsonObject.id] = topologyJsonObject; - this._topologyCatalog[topologyJsonObject.id] = topology; - - return true; - - } - -} diff --git a/src/js/utils/storageManager.js b/src/js/utils/storageManager.js deleted file mode 100644 index 1c533e9..0000000 --- a/src/js/utils/storageManager.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const log4js = require('log4js'); - -// Création du LOGGER -var LOGGER = log4js.getLogger("STORAGEMANAGER"); - -module.exports = { - - /** - * - * @function - * @name checkJsonStorage - * @description Fonction utilisée pour vérifier l'écriture d'un json au niveau du stockage. - * @param {json} jsonStorage - Json décrivant le stockage - * @return {boolean} vrai si tout c'est bien passé et faux s'il y a eu une erreur - * - */ - - checkJsonStorage: function(jsonStorage) { - - let storageFound = false; - - // On regarde si c'est du stokage fichier - if (jsonStorage.file) { - // Vérification que le fichier existe et peut être lu. - if (fs.existsSync(jsonStorage.file)) { - try { - fs.accessSync(jsonStorage.file, fs.constants.R_OK); - } catch (err) { - LOGGER.error("Le fichier " + jsonStorage.file + " ne peut etre lu."); - return false; - } - storageFound = true; - } else { - LOGGER.error("Le fichier " + jsonStorage.file + " n'existe pas."); - return false; - } - } else if (jsonStorage.tar || jsonStorage.config) { - // Vérification que le fichier existe et peut être lu. - if (fs.existsSync(jsonStorage.tar)) { - try { - fs.accessSync(jsonStorage.tar, fs.constants.R_OK); - } catch (err) { - LOGGER.error("Le fichier " + jsonStorage.tar + " ne peut etre lu."); - return false; - } - storageFound = true; - } else { - LOGGER.error("Le fichier " + jsonStorage.tar + " n'existe pas."); - return false; - } - } else if (jsonStorage.config) { - // Vérification que le fichier existe et peut être lu. - if (fs.existsSync(jsonStorage.config)) { - try { - fs.accessSync(jsonStorage.config, fs.constants.R_OK); - } catch (err) { - LOGGER.error("Le fichier " + jsonStorage.config + " ne peut etre lu."); - return false; - } - storageFound = true; - } else { - LOGGER.error("Le fichier " + jsonStorage.config + " n'existe pas."); - return false; - } - } else if (jsonStorage.costColumn) { - // TODO: Pas trop possible de vérifier sans requête - storageFound = true; - } else if (jsonStorage.rcostColumn) { - // TODO: Pas trop possible de vérifier sans requête - storageFound = true; - } else if (jsonStorage.url) { - // TODO: Pas trop possible de vérifier sans requête - storageFound = true; - } else { - // on va tester d'autres types - } - - return storageFound; - } - -} diff --git a/test/integration/readme.md b/test/integration/readme.md index b60d3b8..3d54512 100644 --- a/test/integration/readme.md +++ b/test/integration/readme.md @@ -20,8 +20,6 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - routeRequest (request) - isochroneRequest (request) - serverManager (server, ExpressJS, log4js) - - osmTopology (topology) - - dbTopology (topology, base) - Deuxième niveau: - resourceOperation (resourceParameter) @@ -32,8 +30,7 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - constraintParameter (resourceParameter, constraint, looseConstraint) - isochroneResponse (response, point, geometry) - step (line, duration, distance) - - topologyManager (osmTopology, dbTopology, storageManager, baseManager, projectionManager, log4js) - - source (osmTopology, bdTopology, ) + - source (baseManager, projectionManager) - Troisième niveau: - parameterManager (parameter, boolParameter, enumParameter, floatParameter, pointParameter, constraintParameter, log4js) @@ -50,17 +47,17 @@ Cinquième niveau: - routeResponse (response, point, route) Sixième niveau: - - osrmSource (source, osrm, osmTopology, routeResponse, route, portion, line, point, step, distance, duration, errorManager, log4js) - - pgrSource (source, dbTopology, routeResponse, isochroneResponse, route, portion, line, point, polygon, step, distance, duration, errorManager, gisManager, copyManager, simplify, turf, looseConstraint, log4js) + - osrmSource (source, osrm, routeResponse, route, portion, line, point, step, distance, duration, errorManager, log4js) + - pgrSource (source, routeResponse, isochroneResponse, route, portion, line, point, polygon, step, distance, duration, errorManager, gisManager, copyManager, simplify, turf, looseConstraint, log4js) Septième niveau: - - sourceManager (osrmSource, pgrSource, errorManager, storageManager, operationManager, osmTopology, dbTopology, log4js) + - sourceManager (osrmSource, pgrSource, errorManager, storageManager, operationManager, log4js) Huitième niveau: - resourceManager (osrmResource, pgrResource, sourceManager, operationManager, log4js) Neuvième niveau: - - service (apisManager, resourceManager, sourceManager, operationManager, baseManager, topologyManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) + - service (apisManager, resourceManager, sourceManager, operationManager, baseManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) Autres: - road2.js From 7aad581e68379a55758729ab348dd8dfe2cbceb4 Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 4 Nov 2022 13:16:10 +0100 Subject: [PATCH 13/93] WIP: modification des ressources pour pointer sur les bonnes sources --- .../simple/1.0.0/controller/controller.js | 27 ++- src/js/resources/osrmResource.js | 98 +++++++--- src/js/resources/pgrResource.js | 113 +++++++----- src/js/resources/resource.js | 32 ++++ src/js/resources/resourceManager.js | 18 +- src/js/resources/smartpgrResource.js | 171 ++++++++++++------ src/js/resources/valhallaResource.js | 112 ++++++++---- src/js/service/service.js | 28 --- src/js/sources/sourceManager.js | 20 ++ 9 files changed, 420 insertions(+), 199 deletions(-) diff --git a/src/js/apis/simple/1.0.0/controller/controller.js b/src/js/apis/simple/1.0.0/controller/controller.js index d5b298f..98d99b1 100644 --- a/src/js/apis/simple/1.0.0/controller/controller.js +++ b/src/js/apis/simple/1.0.0/controller/controller.js @@ -185,13 +185,6 @@ module.exports = { } - - // Vérification de la validité du profile et de sa compatibilité avec l'optimisation - if (!resource.linkedSource[profile+optimization]) { - throw errorManager.createError(" Parameters 'profile' and 'optimization' are not compatible ", 400); - } else { - LOGGER.debug("profile et optimization compatibles"); - } // --- @@ -200,6 +193,12 @@ module.exports = { LOGGER.debug(routeRequest); + // Vérification de la validité du profile et de sa compatibilité avec l'optimisation + if (!resource.checkSourceAvailibilityFromRequest(routeRequest)) { + throw errorManager.createError(" Parameters 'profile' and 'optimization' are not compatible ", 400); + } else { + LOGGER.debug("profile et optimization compatibles"); + } // On va vérifier la présence des paramètres non obligatoires pour l'API et l'objet RouteRequest @@ -850,13 +849,6 @@ module.exports = { } - /* Vérification de la validité du profile et de sa compatibilité avec le costType. */ - if (!resource.linkedSource[profile+costType]) { - throw errorManager.createError("Parameters 'profile' and 'costType' are not compatible.", 400); - } else { - LOGGER.debug("Parameters 'profile' and 'costType' are compatible"); - } - /* Paramètre 'direction'. */ if (parameters.direction) { @@ -914,6 +906,13 @@ module.exports = { distanceUnit ); + /* Vérification de la validité du profile et de sa compatibilité avec le costType. */ + if (!resource.checkSourceAvailibilityFromRequest(isochroneRequest)) { + throw errorManager.createError("Parameters 'profile' and 'costType' are not compatible.", 400); + } else { + LOGGER.debug("Parameters 'profile' and 'costType' are compatible"); + } + // Contraintes // --- if (parameters.constraints) { diff --git a/src/js/resources/osrmResource.js b/src/js/resources/osrmResource.js index d202ea5..2f4183a 100644 --- a/src/js/resources/osrmResource.js +++ b/src/js/resources/osrmResource.js @@ -33,19 +33,9 @@ module.exports = class osrmResource extends Resource { // Correspondance entre profile/optimization et sourceId this._linkedSource = {}; - // Instanciation de la correspondance entre profile/optimization et sourceId - // et instanciation du profile et de l'optimisation par défaut - for (let i=0; i < this._configuration.sources.length; i++) { - - let linkedId = this._configuration.sources[i].cost.profile + this._configuration.sources[i].cost.optimization; - this._linkedSource[linkedId] = this._configuration.sources[i].id; - - } - - // id de la source utilisée pour l'opération nearest - // on prend la première de la configuration - // TODO : à voir si on rend cela plus configurable - this._nearestSource = this._configuration.sources[0].id; + // Correspondance pour l'opération nearest + // Il s'agit de l'id de la source utilisée pour l'opération nearest + this._nearestSource = ""; // Attribut des voies // Par défaut, OSRM ne renvoit que le nom des voies empruntées. @@ -68,23 +58,60 @@ module.exports = class osrmResource extends Resource { /** * * @function - * @name get linkedSource - * @description Récupérer la correspondance entre profile/optimization et sourceId de la ressource + * @name get waysAttributes + * @description Récupérer la liste des attributs disponibles pour les voies empruntées. * */ - get linkedSource () { - return this._linkedSource; + get waysAttributes () { + return this._waysAttributes; } /** * * @function - * @name get waysAttributes - * @description Récupérer la liste des attributs disponibles pour les voies empruntées. + * @name initResource + * @description Créer les liens entre divers éléments d'une ressource et les sources associées + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {SourceManager} sourceManager - Manager des sources du service + * @return {boolean} * */ - get waysAttributes () { - return this._waysAttributes; + initResource (sourceManager) { + + // Instanciation de la correspondance entre profile/optimization et sourceId + for (let i=0; i < this._configuration.sources.length; i++) { + + if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[i])) { + LOGGER.error("La source n'a pas été chargée"); + return false; + } else { + + LOGGER.debug("La source est bien disponible"); + + let source = sourceManager.getSourceById(this._configuration.sources[i]); + + // TODO : faire cette vérification aussi pendant le check de la ressource + if (source.type !== "osrm") { + LOGGER.error("La source n'est pas de type 'osrm'"); + return false; + } + + let linkedId = source.configuration.cost.profile + source.configuration.cost.optimization; + this._linkedSource[linkedId] = source.configuration.id; + + if (i === 0) { + // Gestion de nearest + // on prend la première de la configuration + // TODO : à voir si on rend cela plus configurable + this._nearestSource = source.configuration.id; + } + + } + + } + + return true; + } /** @@ -124,9 +151,7 @@ module.exports = class osrmResource extends Resource { */ getSourceIdFromRequest (request) { - const currentOperation = request.operation; - - if (currentOperation === "nearest") { + if (request.operation === "nearest") { return this._nearestSource; } else { if (this._linkedSource[request.profile+request.optimization]) { @@ -136,7 +161,32 @@ module.exports = class osrmResource extends Resource { } } + } + + /** + * + * @function + * @name checkSourceAvailibilityFromRequest + * @description Savoir s'il y a une source disponible pour répondre à la requête. Par exemple, pour un itinéraire, il s'agira de savoir si un couple profile/optimization est disponible. + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {Request} request - Objet Request ou ou dérivant de la classe Request + * @return {boolean} + * + */ + checkSourceAvailibilityFromRequest (request) { + if (request.operation === "nearest") { + // On utilise toujours la première source. Il y en a forcément une. + return true; + } else if (request.operation === "route") { + if (this._linkedSource[request.profile+request.optimization]) { + return true; + } else { + return false; + } + } else { + return false; + } } diff --git a/src/js/resources/pgrResource.js b/src/js/resources/pgrResource.js index 1a26e1b..b33a892 100644 --- a/src/js/resources/pgrResource.js +++ b/src/js/resources/pgrResource.js @@ -36,32 +36,6 @@ module.exports = class pgrResource extends Resource { // Correspondance entre profile/optimization et sourceId this._linkedSource = {}; - // Instanciation de la correspondance entre profile/optimization et sourceId - // et instanciation du profile et de l'optimisation par défaut - for (let i=0; i < this._configuration.sources.length; i++) { - - /* TODO: Il serait mieux, dans le futur, d'avoir un nouveau type de ressource, dédiée à l'isochrone. */ - const currentSourceOptimization = this._configuration.sources[i].cost.optimization; - - let linkedId = ''; - if (operations["isochrone"]) { - if (currentSourceOptimization === "fastest") { - linkedId = this._configuration.sources[i].cost.profile + "time"; - } else if (currentSourceOptimization === "shortest") { - linkedId = this._configuration.sources[i].cost.profile + "distance"; - } else { - /* TODO: À repenser. */ - } - - this._linkedSource[linkedId] = this._configuration.sources[i].id; - } - if (operations["route"]) { - linkedId = this._configuration.sources[i].cost.profile + this._configuration.sources[i].cost.optimization; - - this._linkedSource[linkedId] = this._configuration.sources[i].id; - } - } - } /** @@ -78,23 +52,44 @@ module.exports = class pgrResource extends Resource { /** * * @function - * @name get linkedSource - * @description Récupérer la correspondance entre profile/optimization et sourceId de la ressource + * @name initResource + * @description Créer les liens entre divers éléments d'une ressource et les sources associées + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {SourceManager} sourceManager - Manager des sources du service + * @return {boolean} * */ - get linkedSource () { - return this._linkedSource; - } + initResource (sourceManager) { - /** - * - * @function - * @name set linkedSource - * @description Attribuer la correspondance entre profile/optimization et sourceId de la ressource - * - */ - set linkedSource (ls) { - this._linkedSource = ls; + // Instanciation de la correspondance entre profile/optimization et sourceId + for (let i=0; i < this._configuration.sources.length; i++) { + + if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[i])) { + LOGGER.error("La source n'a pas été chargée"); + return false; + } else { + + LOGGER.debug("La source est bien disponible"); + + let source = sourceManager.getSourceById(this._configuration.sources[i]); + + // TODO : faire cette vérification aussi pendant le check de la ressource + if (source.type !== "pgr") { + LOGGER.error("La source n'est pas de type 'pgr'"); + return false; + } + + let linkedIdRoute = source.configuration.cost.profile + source.configuration.cost.optimization; + let linkedIdIso = source.configuration.cost.profile + source.configuration.cost.costType; + this._linkedSource[linkedIdRoute] = source.configuration.id; + this._linkedSource[linkedIdIso] = source.configuration.id; + + } + + } + + return true; + } /** @@ -109,14 +104,14 @@ module.exports = class pgrResource extends Resource { */ getSourceIdFromRequest (request) { - const currentOperation = request.operation; let source = ""; - /* TODO: Pour le moment, c'est un contrôle en dur sur le type de l'opération. Il serait mieux de revoir cette façon de voir (avoir peut-être un catalogue de correspondance ? Maybe..). */ - if (currentOperation === "isochrone") { + if (request.operation === "isochrone") { source = request.profile + request.costType; - } else { + } else if (request.operation === "route") { source = request.profile + request.optimization; + } else { + return null; } if (this._linkedSource[source]) { @@ -127,4 +122,34 @@ module.exports = class pgrResource extends Resource { } + /** + * + * @function + * @name checkSourceAvailibilityFromRequest + * @description Savoir s'il y a une source disponible pour répondre à la requête. Par exemple, pour un itinéraire, il s'agira de savoir si un couple profile/optimization est disponible. + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {Request} request - Objet Request ou ou dérivant de la classe Request + * @return {boolean} + * + */ + checkSourceAvailibilityFromRequest (request) { + + let source = ""; + + if (request.operation === "isochrone") { + source = request.profile + request.costType; + } else if (request.operation === "route") { + source = request.profile + request.optimization; + } else { + return false; + } + + if (this._linkedSource[source]) { + return true; + } else { + return false; + } + + } + } diff --git a/src/js/resources/resource.js b/src/js/resources/resource.js index 354ae65..73fd087 100644 --- a/src/js/resources/resource.js +++ b/src/js/resources/resource.js @@ -98,6 +98,38 @@ module.exports = class Resource { return sourceId; } + /** + * + * @function + * @name checkSourceAvailibilityFromRequest + * @description Savoir s'il y a une source disponible pour répondre à la requête. Par exemple, pour un itinéraire, il s'agira de savoir si un couple profile/optimization est disponible. + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * Dans la classe actuelle, ce n'est que pour indiquer qu'il faut implémenter la fonction + * dans chacune des classes filles. + * @param {Request} request - Objet Request ou ou dérivant de la classe Request + * @return {boolean} + * + */ + checkSourceAvailibilityFromRequest (request) { + return false; + } + + /** + * + * @function + * @name initResource + * @description Créer les liens entre divers éléments d'une ressource et les sources associées + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * Dans la classe actuelle, ce n'est que pour indiquer qu'il faut implémenter la fonction + * dans chacune des classes filles. + * @param {SourceManager} sourceManager - Manager des sources du service + * @return {boolean} + * + */ + initResource (sourceManager) { + return false; + } + /** * * @function diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index 9a80015..0c24926 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -216,7 +216,17 @@ module.exports = class resourceManager { return false; } else { - LOGGER.info("Verification des sources...") + LOGGER.info("Verification des sources..."); + + if (Array.isArray(resourceJsonObject.resource.sources)) { + LOGGER.error("Mauvaise configuration: 'resource.sources' n'est pas un tableau"); + return false; + } + + if (resourceJsonObject.resource.sources.length === 0) { + LOGGER.error("Mauvaise configuration: 'resource.sources' est un tableau vide"); + return false; + } for (let i = 0; i < resourceJsonObject.resource.sources.length; i++ ) { @@ -392,6 +402,12 @@ module.exports = class resourceManager { return false; } + // Initialisation de la correspondance entre ressource et sources + if (!resource.initResource(this._sourceManager)) { + LOGGER.error("Impossible d'instancier les liens avec les sources"); + return false; + } + // on sauvegarde l'id de la ressource pour savoir qu'elle a déjà été créée et la ressource elle-même this._loadedResourceId.push(resourceJsonObject.resource.id); this._resource[resourceJsonObject.resource.id] = resource; diff --git a/src/js/resources/smartpgrResource.js b/src/js/resources/smartpgrResource.js index c08a9e6..77252d9 100644 --- a/src/js/resources/smartpgrResource.js +++ b/src/js/resources/smartpgrResource.js @@ -36,45 +36,13 @@ module.exports = class smartpgrResource extends Resource { this._configuration = resourceJsonObject.resource; // Seuils de switch entre les sources smartrouting et pgr - this._distThreshold = this._configuration.threshold; // en metres - this._timeThresholdCar = (this._distThreshold / 130000) * 3600; // en secondes - this._timeThresholdPedestrian = (this._distThreshold / 4000) * 3600; // en secondes + this._distThreshold; // en metres + this._timeThresholdCar; // en secondes + this._timeThresholdPedestrian; // en secondes // Correspondance entre profile/optimization et sourceId this._linkedSource = {}; - // Instanciation de la correspondance entre profile/optimization et sourceId - // et instanciation du profile et de l'optimisation par défaut - for (let i=0; i < this._configuration.sources.length; i++) { - - // on recupere l'id de l'unique source smartrouting - if (this._configuration.sources[i].type === 'smartrouting') { - this._linkedSource['smartrouting'] = this._configuration.sources[i].id; - continue; - } - - /* TODO: Il serait mieux, dans le futur, d'avoir un nouveau type de ressource, dédiée à l'isochrone. */ - const currentSourceOptimization = this._configuration.sources[i].cost.optimization; - - let linkedId = ''; - if (operations["isochrone"]) { - if (currentSourceOptimization === "fastest") { - linkedId = this._configuration.sources[i].cost.profile + "time"; - } else if (currentSourceOptimization === "shortest") { - linkedId = this._configuration.sources[i].cost.profile + "distance"; - } else { - /* TODO: À repenser. */ - } - - this._linkedSource[linkedId] = this._configuration.sources[i].id; - } - if (operations["route"]) { - linkedId = this._configuration.sources[i].cost.profile + this._configuration.sources[i].cost.optimization; - - this._linkedSource[linkedId] = this._configuration.sources[i].id; - } - } - } /** @@ -91,23 +59,62 @@ module.exports = class smartpgrResource extends Resource { /** * * @function - * @name get linkedSource - * @description Récupérer la correspondance entre profile/optimization et sourceId de la ressource + * @name initResource + * @description Créer les liens entre divers éléments d'une ressource et les sources associées + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {SourceManager} sourceManager - Manager des sources du service + * @return {boolean} * */ - get linkedSource () { - return this._linkedSource; - } + initResource (sourceManager) { - /** - * - * @function - * @name set linkedSource - * @description Attribuer la correspondance entre profile/optimization et sourceId de la ressource - * - */ - set linkedSource (ls) { - this._linkedSource = ls; + // Seuils de switch entre les sources smartrouting et pgr + this._distThreshold = this._configuration.threshold; // en metres + this._timeThresholdCar = (this._distThreshold / 130000) * 3600; // en secondes + this._timeThresholdPedestrian = (this._distThreshold / 4000) * 3600; // en secondes + + // Instanciation de la correspondance entre profile/optimization et sourceId + for (let i=0; i < this._configuration.sources.length; i++) { + + if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[i])) { + + LOGGER.error("La source n'a pas été chargée"); + return false; + + } else { + + LOGGER.debug("La source est bien disponible"); + + let source = sourceManager.getSourceById(this._configuration.sources[i]); + + + + // on recupere l'id de l'unique source smartrouting + if (source.type === 'smartrouting') { + + this._linkedSource['smartrouting'] = source.configuration.id; + + } else if (source.type === 'pgr') { + + let linkedIdRoute = source.configuration.cost.profile + source.configuration.cost.optimization; + let linkedIdIso = source.configuration.cost.profile + source.configuration.cost.costType; + this._linkedSource[linkedIdRoute] = source.configuration.id; + this._linkedSource[linkedIdIso] = source.configuration.id; + + } else { + + // TODO : faire cette vérification aussi pendant le check de la ressource + LOGGER.error("La source n'est pas de type 'pgr' ou 'smartrouting'"); + return false; + + } + + } + + } + + return true; + } /** @@ -122,13 +129,12 @@ module.exports = class smartpgrResource extends Resource { */ getSourceIdFromRequest (request) { - const currentOperation = request.operation; let source = ""; - /*TODO: Pour le moment, c'est un contrôle en dur sur le type de l'opération. Il serait mieux de revoir cette façon de voir (avoir peut-être un catalogue de correspondance ? Maybe..). */ - if (currentOperation === "isochrone") { + if (request.operation === "isochrone") { let useSmartrouting = false; + if (request.costType === 'distance') { // Note costValue est déjà en metres useSmartrouting = request.costValue > this._distThreshold; @@ -146,8 +152,14 @@ module.exports = class smartpgrResource extends Resource { } else { source = request.profile + request.costType; } - } else { + + } else if (request.operation === "route") { + + // On décide de faire uniquement du PGR sur cette ressource dans le cas de l'iti source = request.profile + request.optimization; + + } else { + return null; } if (this._linkedSource[source]) { @@ -158,4 +170,57 @@ module.exports = class smartpgrResource extends Resource { } + /** + * + * @function + * @name checkSourceAvailibilityFromRequest + * @description Savoir s'il y a une source disponible pour répondre à la requête. Par exemple, pour un itinéraire, il s'agira de savoir si un couple profile/optimization est disponible. + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {Request} request - Objet Request ou ou dérivant de la classe Request + * @return {boolean} + * + */ + checkSourceAvailibilityFromRequest (request) { + + let source = ""; + + if (request.operation === "isochrone") { + + let useSmartrouting = false; + + if (request.costType === 'distance') { + // Note costValue est déjà en metres + useSmartrouting = request.costValue > this._distThreshold; + } else if (request.costType === 'time') { + // Note costValue est déjà en secondes + if (request.profile === 'car') { + useSmartrouting = request.costValue > this._timeThresholdCar + } else if (request.profile === 'pedestrian') { + useSmartrouting = request.costValue > this._timeThresholdPedestrian + } + } + + if (useSmartrouting) { + source = 'smartrouting'; + } else { + source = request.profile + request.costType; + } + + } else if (request.operation === "route") { + + // On décide de faire uniquement du PGR sur cette ressource dans le cas de l'iti + source = request.profile + request.optimization; + + } else { + return false; + } + + if (this._linkedSource[source]) { + return true; + } else { + return false; + } + + } + } diff --git a/src/js/resources/valhallaResource.js b/src/js/resources/valhallaResource.js index f470787..4ec0229 100644 --- a/src/js/resources/valhallaResource.js +++ b/src/js/resources/valhallaResource.js @@ -33,29 +33,6 @@ module.exports = class valhallaResource extends Resource { // Correspondance entre profile/optimization et sourceId this._linkedSource = {}; - // Instanciation de la correspondance entre profile/optimization et sourceId - // et instanciation du profile et de l'optimisation par défaut - for (let i=0; i < this._configuration.sources.length; i++) { - - /* TODO: Il serait mieux, dans le futur, d'avoir un nouveau type de ressource, dédiée à l'isochrone. */ - const currentSourceOptimization = this._configuration.sources[i].cost.optimization; - let linkedId = ''; - if (operations["isochrone"]) { - if (currentSourceOptimization === "fastest") { - linkedId = this._configuration.sources[i].cost.profile + "time"; - } else if (currentSourceOptimization === "shortest") { - linkedId = this._configuration.sources[i].cost.profile + "distance"; - } else { - /* TODO: À repenser. */ - } - this._linkedSource[linkedId] = this._configuration.sources[i].id; - } - if (operations["route"]) { - linkedId = this._configuration.sources[i].cost.profile + this._configuration.sources[i].cost.optimization; - this._linkedSource[linkedId] = this._configuration.sources[i].id; - } - } - // Attribut des voies // Par défaut, OSRM ne renvoit que le nom des voies empruntées. this._waysAttributes = new Array(); @@ -77,23 +54,55 @@ module.exports = class valhallaResource extends Resource { /** * * @function - * @name get linkedSource - * @description Récupérer la correspondance entre profile/optimization et sourceId de la ressource + * @name get waysAttributes + * @description Récupérer la liste des attributs disponibles pour les voies empruntées. * */ - get linkedSource () { - return this._linkedSource; + get waysAttributes () { + return this._waysAttributes; } /** * * @function - * @name get waysAttributes - * @description Récupérer la liste des attributs disponibles pour les voies empruntées. + * @name initResource + * @description Créer les liens entre divers éléments d'une ressource et les sources associées + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {SourceManager} sourceManager - Manager des sources du service + * @return {boolean} * */ - get waysAttributes () { - return this._waysAttributes; + initResource (sourceManager) { + + // Instanciation de la correspondance entre profile/optimization et sourceId + for (let i=0; i < this._configuration.sources.length; i++) { + + if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[i])) { + LOGGER.error("La source n'a pas été chargée"); + return false; + } else { + + LOGGER.debug("La source est bien disponible"); + + let source = sourceManager.getSourceById(this._configuration.sources[i]); + + // TODO : faire cette vérification aussi pendant le check de la ressource + if (source.type !== "valhalla") { + LOGGER.error("La source n'est pas de type 'valhalla'"); + return false; + } + + let linkedIdRoute = source.configuration.cost.profile + source.configuration.cost.optimization; + let linkedIdIso = source.configuration.cost.profile + source.configuration.cost.costType; + this._linkedSource[linkedIdRoute] = source.configuration.id; + this._linkedSource[linkedIdIso] = source.configuration.id; + + } + + } + + return true; + } /** @@ -133,19 +142,52 @@ module.exports = class valhallaResource extends Resource { */ getSourceIdFromRequest (request) { - const currentOperation = request.operation; let source = ""; - /* TODO: Pour le moment, c'est un contrôle en dur sur le type de l'opération. Il serait mieux de revoir cette façon de voir (avoir peut-être un catalogue de correspondance ? Maybe..). */ - if (currentOperation === "isochrone") { + + if (request.operation === "isochrone") { source = request.profile + request.costType; - } else { + } else if (request.operation === "route") { source = request.profile + request.optimization; + } else { + return null; } + if (this._linkedSource[source]) { return this._linkedSource[source]; } else { return null; } + + } + + /** + * + * @function + * @name checkSourceAvailibilityFromRequest + * @description Savoir s'il y a une source disponible pour répondre à la requête. Par exemple, pour un itinéraire, il s'agira de savoir si un couple profile/optimization est disponible. + * Ce traitement est placé ici car c'est la ressource qui sait quelle source est concernée par la requête. + * @param {Request} request - Objet Request ou ou dérivant de la classe Request + * @return {boolean} + * + */ + checkSourceAvailibilityFromRequest (request) { + + let source = ""; + + if (request.operation === "isochrone") { + source = request.profile + request.costType; + } else if (request.operation === "route") { + source = request.profile + request.optimization; + } else { + return false; + } + + if (this._linkedSource[source]) { + return true; + } else { + return false; + } + } } diff --git a/src/js/service/service.js b/src/js/service/service.js index 85f4e81..096314f 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -175,34 +175,6 @@ module.exports = class Service { } } - /** - * - * @function - * @name getSourceById - * @description Récupérer une source par son id - * @param {string} id - Id de la source - * - */ - getSourceById(id) { - return this._sourceManager.source[id]; - } - - /** - * - * @function - * @name verifySourceExistenceById - * @description Savoir si une source existe à partir de son id - * @param {string} id - Id de la source - * - */ - verifySourceExistenceById(id) { - if (this._sourceManager.source[id]) { - return true; - } else { - return false; - } - } - /** * * @function diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index b9f32f8..9939818 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -99,6 +99,26 @@ module.exports = class sourceManager { } + /** + * + * @function + * @name getSourceById + * @description Fonction utilisée pour récupérer une source + * @param {string} id - Id de la source + * @return {source} source - Instance fille de la classe Source + * + */ + + getSourceById(id) { + + if (this.isLoadedSourceAvailable(id)) { + return this._source[id]; + } else { + return null; + } + + } + /** * * @function From 933778c193c6fefe41eede65fe1468125e7c4319 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 14 Nov 2022 13:51:49 +0100 Subject: [PATCH 14/93] =?UTF-8?q?fin=20des=20devs=20pour=20s=C3=A9parer=20?= =?UTF-8?q?les=20sources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/dev/docker-compose.yml | 2 +- src/js/geography/projectionManager.js | 27 +++++++++------ src/js/operations/operationManager.js | 5 +++ src/js/resources/osrmResource.js | 4 +++ src/js/resources/pgrResource.js | 22 +++++++----- src/js/resources/resourceManager.js | 49 ++++++++++++++++----------- src/js/resources/smartpgrResource.js | 15 +++++--- src/js/resources/valhallaResource.js | 24 +++++++++---- src/js/sources/pgrSource.js | 6 +++- src/js/sources/smartroutingSource.js | 2 +- src/js/sources/source.js | 22 ++++++++++++ src/js/sources/sourceManager.js | 23 +++++-------- 12 files changed, 133 insertions(+), 68 deletions(-) diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index d4756d8..b0edc31 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -11,7 +11,7 @@ services: - pgrouting environment: - NODE_ENV=debug - command : "sleep 60000" + command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" ports: - 8080:8080 - 9229:9229 diff --git a/src/js/geography/projectionManager.js b/src/js/geography/projectionManager.js index a58c178..fdd4a88 100644 --- a/src/js/geography/projectionManager.js +++ b/src/js/geography/projectionManager.js @@ -112,7 +112,8 @@ module.exports = class ProjectionManager { */ checkBboxConfiguration (bbox, projectionId) { - LOGGER.info("Vérification d'une bbox..."); + LOGGER.info("Vérification d'une bbox... "); + LOGGER.debug("bbox:'" + bbox+"'"); if (!bbox) { LOGGER.error("Aucune bbox fournie"); @@ -124,27 +125,31 @@ module.exports = class ProjectionManager { return false; } - let regex = new RegExp("^(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*)$"); - if (!regex.test(bbox)) { - LOGGER.error("La bbox n'est pas correctement formattée"); - return false; - } - - let bboxArray = new Array(4); + let bboxArray = new Array(); try { - bboxArray = regex.exec(bbox); + bboxArray = bbox.match(/^(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*)$/); } catch(error) { LOGGER.error("Impossible d'analyser la bbox"); LOGGER.error(error); return false; } - if (bboxArray[0] >= bboxArray[2]) { - LOGGER.error("Mauvaise configuration : Xmin est supérieur ou égal à Xmax"); + if (bboxArray === null) { + LOGGER.error("La bbox n'est pas correctement formattée : aucune correspondance n'a été trouvée"); + return false; + } + + if (bboxArray.length !== 5) { + LOGGER.error("La bbox n'est pas correctement formattée : l'ensemble des correspondances n'a pas été correctement identifié"); return false; } if (bboxArray[1] >= bboxArray[3]) { + LOGGER.error("Mauvaise configuration : Xmin est supérieur ou égal à Xmax"); + return false; + } + + if (bboxArray[2] >= bboxArray[4]) { LOGGER.error("Mauvaise configuration : Ymin est supérieur ou égal à Ymax"); return false; } diff --git a/src/js/operations/operationManager.js b/src/js/operations/operationManager.js index 919f1aa..ca6528c 100644 --- a/src/js/operations/operationManager.js +++ b/src/js/operations/operationManager.js @@ -401,6 +401,11 @@ module.exports = class operationManager { LOGGER.info("Verification de l'operation de la ressource..."); + if (!Array.isArray(resourceOperationJsonObject)) { + LOGGER.error("Mauvais configuration : la configuration d'opérations fournie n'est pas un tableau"); + return false; + } + // on regarde d'abord la taille du tableau donné en entrée if (resourceOperationJsonObject.length === 0) { LOGGER.error("Il n'y aucune operation decrite"); diff --git a/src/js/resources/osrmResource.js b/src/js/resources/osrmResource.js index 2f4183a..d4fa9e0 100644 --- a/src/js/resources/osrmResource.js +++ b/src/js/resources/osrmResource.js @@ -1,6 +1,10 @@ 'use strict'; const Resource = require('./resource'); +const log4js = require('log4js'); + +// Création du LOGGER +var LOGGER = log4js.getLogger("OSRMRESOURCE"); /** * diff --git a/src/js/resources/pgrResource.js b/src/js/resources/pgrResource.js index b33a892..b4dbedd 100644 --- a/src/js/resources/pgrResource.js +++ b/src/js/resources/pgrResource.js @@ -1,9 +1,10 @@ 'use strict'; const Resource = require('./resource'); +const log4js = require('log4js'); // Création du LOGGER -const log4js = require('log4js'); +var LOGGER = log4js.getLogger("PGRRESOURCE"); /** * @@ -62,16 +63,16 @@ module.exports = class pgrResource extends Resource { initResource (sourceManager) { // Instanciation de la correspondance entre profile/optimization et sourceId - for (let i=0; i < this._configuration.sources.length; i++) { + for (let s=0; s < this._configuration.sources.length; s++) { - if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[i])) { + if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[s])) { LOGGER.error("La source n'a pas été chargée"); return false; } else { LOGGER.debug("La source est bien disponible"); - let source = sourceManager.getSourceById(this._configuration.sources[i]); + let source = sourceManager.getSourceById(this._configuration.sources[s]); // TODO : faire cette vérification aussi pendant le check de la ressource if (source.type !== "pgr") { @@ -79,14 +80,19 @@ module.exports = class pgrResource extends Resource { return false; } - let linkedIdRoute = source.configuration.cost.profile + source.configuration.cost.optimization; - let linkedIdIso = source.configuration.cost.profile + source.configuration.cost.costType; - this._linkedSource[linkedIdRoute] = source.configuration.id; - this._linkedSource[linkedIdIso] = source.configuration.id; + for (let i = 0; i < source.configuration.costs.length; i++) { + + let linkedIdRoute = source.configuration.costs[i].profile + source.configuration.costs[i].optimization; + let linkedIdIso = source.configuration.costs[i].profile + source.configuration.costs[i].costType; + this._linkedSource[linkedIdRoute] = source.configuration.id; + this._linkedSource[linkedIdIso] = source.configuration.id; + } + } } + return true; diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index 0c24926..ac13fe5 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -31,8 +31,15 @@ module.exports = class resourceManager { // Liste des ressources chargées dans le manager this._resource = {}; - // Liste des types de ressource gérées par le manager - this._availableResourceTypes = ["pgr", "smartpgr","osrm","valhalla"]; + // Correspondance entre les ressources et les opérations possibles + // Le contenu de ce tableau dépend du code écrit dans la ressource correspondante, de ce nous avons choisis de l'implémenter dans Road2 + // Ce tableau peut beaucoup ressembler à son équivalent du sourceManager mais il peut aussi s'en écarter selon les futures ressources qui seront implémentées + this._operationsByType = { + "osrm": ["nearest", "route"], + "pgr": ["route", "isochrone"], + "smartpgr": ["route", "isochrone"], + "valhalla": ["route", "isochrone"] + }; // Manager de source this._sourceManager = sourceManager; @@ -139,8 +146,10 @@ module.exports = class resourceManager { LOGGER.info("Verification de la configuration d'une ressource..."); if (!resourceJsonObject.resource) { - LOGGER.error("Le fichier ne contient pas d'objet resource"); + LOGGER.error("La configuration ne contient pas d'objet resource"); return false; + } else { + LOGGER.debug("La configuration contient bien un objet resource"); } // ID @@ -148,6 +157,7 @@ module.exports = class resourceManager { LOGGER.error("La ressource ne contient pas d'id."); return false; } else { + LOGGER.info("Ressource id: " + resourceJsonObject.resource.id); // On vérifie que l'id de la ressource n'est pas déjà pris par une autre ressource chargée @@ -181,6 +191,9 @@ module.exports = class resourceManager { LOGGER.error("La ressource ne contient pas de version."); return false; } else { + + LOGGER.debug("La ressource contient une version " + resourceJsonObject.resource.resourceVersion); + // on vérifie que c'est bien une string if (typeof resourceJsonObject.resource.resourceVersion !== "string") { LOGGER.error("La version de la ressource n'est pas une chaine de carateres."); @@ -194,8 +207,10 @@ module.exports = class resourceManager { return false; } else { + LOGGER.debug("La ressource contient un type " + resourceJsonObject.resource.type); + // Vérification que le type est valide - if (this._availableResourceTypes.includes(resourceJsonObject.resource.type)) { + if (Object.keys(this._operationsByType).includes(resourceJsonObject.resource.type)) { LOGGER.info("Type de la ressource disponible: " + resourceJsonObject.resource.type); } else { LOGGER.error("La ressource indique un type invalide: " + resourceJsonObject.resource.type); @@ -208,7 +223,9 @@ module.exports = class resourceManager { if (!resourceJsonObject.resource.description) { LOGGER.error("La ressource ne contient pas de description."); return false; - } + } else { + LOGGER.debug("La ressource contient une description"); + } // Sources if (!resourceJsonObject.resource.sources) { @@ -218,7 +235,7 @@ module.exports = class resourceManager { LOGGER.info("Verification des sources..."); - if (Array.isArray(resourceJsonObject.resource.sources)) { + if (!Array.isArray(resourceJsonObject.resource.sources)) { LOGGER.error("Mauvaise configuration: 'resource.sources' n'est pas un tableau"); return false; } @@ -232,13 +249,16 @@ module.exports = class resourceManager { let sourceId = resourceJsonObject.resource.sources[i]; if (!this._sourceManager.isCheckedSourceAvailable(sourceId)) { - LOGGER.error("La ressource contient une source non disponible."); + LOGGER.error("La ressource contient une source non disponible : " + sourceId); return false; } else { // TODO : on stocke l'id de la ressource pour cette source donnée } } + + LOGGER.debug("Vérification des sources terminée"); + } // availableOperations @@ -258,19 +278,10 @@ module.exports = class resourceManager { for (let i = 0; i < resourceJsonObject.resource.availableOperations.length; i++) { let operationId = resourceJsonObject.resource.availableOperations[i].id; - let found = false; - - for (let j = 0; resourceJsonObject.resource.sources; j++) { - let sourceType = resourceJsonObject.resource.sources[j].type; - let operations = this._sourceManager.operationsByType[sourceType]; - if (operations.includes(operationId)) { - found = true; - break; - } - } + let availableOperationsOfType = this._operationsByType[resourceJsonObject.resource.type]; - if (!found) { - LOGGER.error("L'opération " + operationId + " n'a pas de source pour y répondre"); + if (!availableOperationsOfType.includes(operationId)) { + LOGGER.error("L'opération " + operationId + " n'est pas disponible pour le type de ressource " + resourceJsonObject.resource.type); return false; } diff --git a/src/js/resources/smartpgrResource.js b/src/js/resources/smartpgrResource.js index 77252d9..5f273e5 100644 --- a/src/js/resources/smartpgrResource.js +++ b/src/js/resources/smartpgrResource.js @@ -1,7 +1,6 @@ 'use strict'; const Resource = require('./resource'); -const Duration = require('../time/duration'); const log4js = require('log4js'); // Création du LOGGER @@ -96,10 +95,16 @@ module.exports = class smartpgrResource extends Resource { } else if (source.type === 'pgr') { - let linkedIdRoute = source.configuration.cost.profile + source.configuration.cost.optimization; - let linkedIdIso = source.configuration.cost.profile + source.configuration.cost.costType; - this._linkedSource[linkedIdRoute] = source.configuration.id; - this._linkedSource[linkedIdIso] = source.configuration.id; + console.log(source); + + for (let j = 0; j < source.configuration.costs.length; j++) { + + let linkedIdRoute = source.configuration.costs[j].profile + source.configuration.costs[j].optimization; + let linkedIdIso = source.configuration.costs[j].profile + source.configuration.costs[j].costType; + this._linkedSource[linkedIdRoute] = source.configuration.id; + this._linkedSource[linkedIdIso] = source.configuration.id; + + } } else { diff --git a/src/js/resources/valhallaResource.js b/src/js/resources/valhallaResource.js index 4ec0229..340d91a 100644 --- a/src/js/resources/valhallaResource.js +++ b/src/js/resources/valhallaResource.js @@ -1,6 +1,10 @@ 'use strict'; const Resource = require('./resource'); +const log4js = require('log4js'); + +// Création du LOGGER +var LOGGER = log4js.getLogger("VALRESOURCE"); /** * @@ -75,16 +79,16 @@ module.exports = class valhallaResource extends Resource { initResource (sourceManager) { // Instanciation de la correspondance entre profile/optimization et sourceId - for (let i=0; i < this._configuration.sources.length; i++) { + for (let s=0; s < this._configuration.sources.length; s++) { - if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[i])) { + if (!sourceManager.isLoadedSourceAvailable(this._configuration.sources[s])) { LOGGER.error("La source n'a pas été chargée"); return false; } else { LOGGER.debug("La source est bien disponible"); - let source = sourceManager.getSourceById(this._configuration.sources[i]); + let source = sourceManager.getSourceById(this._configuration.sources[s]); // TODO : faire cette vérification aussi pendant le check de la ressource if (source.type !== "valhalla") { @@ -92,15 +96,21 @@ module.exports = class valhallaResource extends Resource { return false; } - let linkedIdRoute = source.configuration.cost.profile + source.configuration.cost.optimization; - let linkedIdIso = source.configuration.cost.profile + source.configuration.cost.costType; - this._linkedSource[linkedIdRoute] = source.configuration.id; - this._linkedSource[linkedIdIso] = source.configuration.id; + for (let i = 0; i < source.configuration.costs.length; i++) { + + let linkedIdRoute = source.configuration.costs[i].profile + source.configuration.costs[i].optimization; + let linkedIdIso = source.configuration.costs[i].profile + source.configuration.costs[i].costType; + this._linkedSource[linkedIdRoute] = source.configuration.id; + this._linkedSource[linkedIdIso] = source.configuration.id; + + } } } + + return true; } diff --git a/src/js/sources/pgrSource.js b/src/js/sources/pgrSource.js index a766888..88c71c0 100644 --- a/src/js/sources/pgrSource.js +++ b/src/js/sources/pgrSource.js @@ -59,6 +59,7 @@ module.exports = class pgrSource extends Source { // Attributs disponibles sur les voies dans la base this._otherAttributes = new Array(); + // TODO : à l'exemple des ressources, faire une fonction init() pour chaque source appelée dans le sourceManager // Création des tableaux d'attributs for (let i = 0; i < sourceJsonObject.storage.base.attributes.length; i++) { let curAttribute = sourceJsonObject.storage.base.attributes[i]; @@ -87,8 +88,11 @@ module.exports = class pgrSource extends Source { // Initialisation des coûts disponibles for (let i = 0; i < sourceJsonObject.costs.length; i++) { - Object.defineProperty(this._costs, sourceJsonObject.costs[i].profile, { value: new Object(), configurable: true, enumerable: true, writable: true }); + if (!this._costs[sourceJsonObject.costs[i].profile]) { + Object.defineProperty(this._costs, sourceJsonObject.costs[i].profile, { value: new Object(), configurable: true, enumerable: true, writable: true }); + } Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile], sourceJsonObject.costs[i].optimization, { value: new Object(), configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile], sourceJsonObject.costs[i].costType, { value: new Object(), configurable: true, enumerable: true, writable: true }); Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].optimization], "costColumn", { value: sourceJsonObject.costs[i].costColumn, configurable: true, enumerable: true, writable: true }); Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].optimization], "rcostColumn", { value: sourceJsonObject.costs[i].rcostColumn, configurable: true, enumerable: true, writable: true }); Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].costType], "costColumn", { value: sourceJsonObject.costs[i].costColumn, configurable: true, enumerable: true, writable: true }); diff --git a/src/js/sources/smartroutingSource.js b/src/js/sources/smartroutingSource.js index 3fc6d2d..31733d3 100644 --- a/src/js/sources/smartroutingSource.js +++ b/src/js/sources/smartroutingSource.js @@ -42,7 +42,7 @@ module.exports = class smartroutingSource extends Source { constructor(sourceJsonObject) { // Constructeur parent - super(sourceJsonObject.id, "pgr", sourceJsonObject.description, sourceJsonObject.projection, sourceJsonObject.bbox); + super(sourceJsonObject.id, "smartrouting", sourceJsonObject.description, sourceJsonObject.projection, sourceJsonObject.bbox); // Stockage de la configuration this._configuration = sourceJsonObject; diff --git a/src/js/sources/source.js b/src/js/sources/source.js index e115204..318f597 100644 --- a/src/js/sources/source.js +++ b/src/js/sources/source.js @@ -69,6 +69,28 @@ module.exports = class Source { return this._type; } + /** + * + * @function + * @name get description + * @description Récupérer la description de la source + * + */ + get description () { + return this._description; + } + + /** + * + * @function + * @name get projection + * @description Récupérer la projection de la source + * + */ + get projection () { + return this._projection; + } + /** * * @function diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index 9939818..fa598af 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -1,5 +1,7 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); const assert = require('assert').strict; const osrmSource = require('../sources/osrmSource'); const pgrSource = require('../sources/pgrSource'); @@ -43,6 +45,8 @@ module.exports = class sourceManager { this._baseManager = baseManager; // Correspondance entre les sources et les opérations possibles + // Le contenu de ce tableau dépend du moteur et du code écrit dans la source correspondante + // Par exemple, le projet OSRM permet de faire du nearest et nous avons choisis de l'implémenter dans Road2 this._operationsByType = { "osrm": ["nearest", "route"], "pgr": ["route", "isochrone"], @@ -63,17 +67,6 @@ module.exports = class sourceManager { return this._source; } - /** - * - * @function - * @name get operationsByType - * @description Récupérer l'ensemble des opérations possibles par type de source - * - */ - get operationsByType() { - return this._operationsByType; - } - /** * * @function @@ -200,7 +193,7 @@ module.exports = class sourceManager { LOGGER.error("La source décrite dans le fichier " + sourceFile + " est mal configuée"); return false; } else { - this._checkedSourceId.push(sourceConf.source.id); + this._checkedSourceId.push(sourceConf.id); } } @@ -529,17 +522,17 @@ module.exports = class sourceManager { for (let j = 0; j < sourceJsonObject.storage.base.attributes.length; j++) { - if (sourceJsonObject.storage.base.attributes[j].key) { + if (!sourceJsonObject.storage.base.attributes[j].key) { LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].key' est absent"); return false; } - if (sourceJsonObject.storage.base.attributes[j].column) { + if (!sourceJsonObject.storage.base.attributes[j].column) { LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].column' est absent"); return false; } - if (sourceJsonObject.storage.base.attributes[j].default) { + if (!sourceJsonObject.storage.base.attributes[j].default) { LOGGER.error("Mauvaise configuration : 'source.storage.base.attributes["+j+"].default' est absent"); return false; } else { From 6a181bd4c2430c08a8f3229ffeda66c294d8138d Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 14 Nov 2022 15:10:27 +0100 Subject: [PATCH 15/93] ajout des tests sur la configuration du service --- src/js/resources/smartpgrResource.js | 2 - .../cucumber/features/conf-service.feature | 71 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/js/resources/smartpgrResource.js b/src/js/resources/smartpgrResource.js index 5f273e5..ddd5037 100644 --- a/src/js/resources/smartpgrResource.js +++ b/src/js/resources/smartpgrResource.js @@ -95,8 +95,6 @@ module.exports = class smartpgrResource extends Resource { } else if (source.type === 'pgr') { - console.log(source); - for (let j = 0; j < source.configuration.costs.length; j++) { let linkedIdRoute = source.configuration.costs[j].profile + source.configuration.costs[j].optimization; diff --git a/test/functional/configuration/cucumber/features/conf-service.feature b/test/functional/configuration/cucumber/features/conf-service.feature index 91f70fe..82991b1 100644 --- a/test/functional/configuration/cucumber/features/conf-service.feature +++ b/test/functional/configuration/cucumber/features/conf-service.feature @@ -399,6 +399,77 @@ Feature: Road2 service configuration # TODO # Tester un dossier de ressources dont une des ressources ne peut être lues + Scenario: [service.json] (sources different) + Given a valid configuration + And with parameter "test" for attribute "application.sources" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:sources:directories' manquant !" + + Scenario: [service.json] (sources vide) + Given a valid configuration + And with parameter "" for attribute "application.sources" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:sources' manquant !" + + Scenario: [service.json] (sources absent) + Given a valid configuration + And without attribute "application.sources" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Objet 'application:sources' manquant !" + + Scenario: [service.json] (sources.directories est une chaine de caracteres) + Given a valid configuration + And with parameter "test" for attribute "application.sources.directories" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:sources:directories' n'est pas un tableau !" + + # TODO + # Tester le paramètre sources.directories sur un tableau vide + + Scenario: [service.json] (sources.directories sur un dossier qui n'existe pas et en chemin relatif) + Given a valid configuration + And with parameter "test" for attribute "application.sources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas:" + + Scenario: [service.json] (sources.directories sur deux dossiers qui n'existent pas et en chemins relatifs) + Given a valid configuration + And with parameter "test" for attribute "application.sources.directories.[0]" in service configuration + And with parameter "test1" for attribute "application.sources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas" + + Scenario: [service.json] (sources.directories contient un élément vide) + Given a valid configuration + And with parameter "" for attribute "application.sources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Mauvaise configuration: Champ 'application:sources:directories' contient un élément vide" + + Scenario: [service.json] (sources.directories contient que des éléments vides) + Given a valid configuration + And with parameter "" for attribute "application.sources.directories.[0]" in service configuration + And with parameter "" for attribute "application.sources.directories.[1]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Aucun dossier de source n'a été validé" + + Scenario: [service.json] (sources.directories absent) + Given a valid configuration + And without attribute "application.sources.directories" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 1 + Then the server log should contain "Mauvaise configuration: Champ 'application:sources:directories' manquant !" + + # TODO + # Tester un dossier de ressources dont une des ressources ne peut être lues + Scenario: [service.json] (network different) Given a valid configuration And with parameter "test" for attribute "application.network" in service configuration From 75407928301e2314c6e680008c5f033f88d52b05 Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 15 Nov 2022 14:41:52 +0100 Subject: [PATCH 16/93] =?UTF-8?q?maj=20de=20la=20doc=20pour=20cette=20s?= =?UTF-8?q?=C3=A9paration=20des=20sources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => administration}/admin_model.yaml | 0 .../{ => pgrouting}/configuration_bdd.json | 0 .../constraints.resource.example | 0 .../{ => projections}/projection_model.yaml | 0 documentation/configuration/readme.md | 30 +- .../configuration/resource_model_osrm.yaml | 146 ---- .../configuration/resource_model_pgr.yaml | 190 ----- .../osrm.resource} | 38 +- .../pgr.resource} | 290 +------- .../resources/resource_model.yaml | 64 ++ .../configuration/resources/smartpgr.resource | 673 ++++++++++++++++++ .../configuration/resources/valhalla.resource | 229 ++++++ .../{ => services}/service_model.yaml | 0 .../configuration/sources/osrm.source | 14 + .../configuration/sources/pgr.source | 200 ++++++ .../configuration/sources/pgrSource.json | 196 ----- .../configuration/sources/smartrouting.source | 10 + .../configuration/sources/source_model.yaml | 43 ++ .../configuration/sources/valhalla.source | 32 + documentation/data/readme.md | 15 +- documentation/developers/concepts.md | 26 +- documentation/developers/readme.md | 2 +- readme.md | 8 +- 23 files changed, 1339 insertions(+), 867 deletions(-) rename documentation/configuration/{ => administration}/admin_model.yaml (100%) rename documentation/configuration/{ => pgrouting}/configuration_bdd.json (100%) rename documentation/configuration/{ => pgrouting}/constraints.resource.example (100%) rename documentation/configuration/{ => projections}/projection_model.yaml (100%) delete mode 100644 documentation/configuration/resource_model_osrm.yaml delete mode 100644 documentation/configuration/resource_model_pgr.yaml rename documentation/configuration/{corse.resource => resources/osrm.resource} (80%) rename documentation/configuration/{bduni_idf_pgr.resource => resources/pgr.resource} (71%) create mode 100644 documentation/configuration/resources/resource_model.yaml create mode 100644 documentation/configuration/resources/smartpgr.resource create mode 100644 documentation/configuration/resources/valhalla.resource rename documentation/configuration/{ => services}/service_model.yaml (100%) create mode 100644 documentation/configuration/sources/osrm.source create mode 100644 documentation/configuration/sources/pgr.source delete mode 100644 documentation/configuration/sources/pgrSource.json create mode 100644 documentation/configuration/sources/smartrouting.source create mode 100644 documentation/configuration/sources/source_model.yaml create mode 100644 documentation/configuration/sources/valhalla.source diff --git a/documentation/configuration/admin_model.yaml b/documentation/configuration/administration/admin_model.yaml similarity index 100% rename from documentation/configuration/admin_model.yaml rename to documentation/configuration/administration/admin_model.yaml diff --git a/documentation/configuration/configuration_bdd.json b/documentation/configuration/pgrouting/configuration_bdd.json similarity index 100% rename from documentation/configuration/configuration_bdd.json rename to documentation/configuration/pgrouting/configuration_bdd.json diff --git a/documentation/configuration/constraints.resource.example b/documentation/configuration/pgrouting/constraints.resource.example similarity index 100% rename from documentation/configuration/constraints.resource.example rename to documentation/configuration/pgrouting/constraints.resource.example diff --git a/documentation/configuration/projection_model.yaml b/documentation/configuration/projections/projection_model.yaml similarity index 100% rename from documentation/configuration/projection_model.yaml rename to documentation/configuration/projections/projection_model.yaml diff --git a/documentation/configuration/readme.md b/documentation/configuration/readme.md index ed6e663..3352638 100644 --- a/documentation/configuration/readme.md +++ b/documentation/configuration/readme.md @@ -13,22 +13,23 @@ Ce fichier va indiquer plusieurs informations et deux éléments : - un *log4js.json* pour les logs de l'administrateur Chaque *service.json* va indiquer les éléments suivants : -- un *log4js.json* pour les logs de l'administrateur et un par service, +- un *log4js.json* par service pour les logs, - un *cors.json* par service si on souhaite spécifier une politique de CORS, - le dossier des *projections*, -- les dossiers des *ressources*. +- les dossiers des *ressources*, +- les dossiers des *sources*. ## administration.json Ce fichier indique quelques informations générales liées à l'instance d'administration. Son principal objectif est l'indication des logs et des services gérés. -On peut trouver un [exemple](../../docker/config/road2.json) de ce fichier et le [modèle](./admin_model.yaml) au format YAML. +On peut trouver un [exemple](../../docker/config/road2.json) de ce fichier et le [modèle](./administration/administration_model.yaml) au format YAML. ## service.json -Ce fichier indique quelques informations générales liées à l'instance de Road2. Son principal objectif est l'indication des logs et des ressources du serveur. Néanmoins, il permet de préciser beaucoup plus d'informations, comme les opérations ou les projections disponibles sur l'instance. +Ce fichier indique quelques informations générales liées à l'instance d'un service. Son principal objectif est l'indication des logs, des sources et des ressources du serveur. Néanmoins, il permet de préciser beaucoup plus d'informations, comme les opérations ou les projections disponibles sur l'instance. -On peut trouver un [exemple](../../docker/config/service.json) de ce fichier et le [modèle](./service_model.yaml) au format YAML. +On peut trouver un [exemple](../../docker/config/service.json) de ce fichier et le [modèle](./services/service_model.yaml) au format YAML. ## log4js.json @@ -46,25 +47,24 @@ On peut trouver un [exemple](../../docker/config/cors.json) de ce fichier au for ## Les projections -Le fichier *server.json* indique un dossier de projections. Ce dossier peut contenir plusieurs fichiers JSON. Ces fichiers seront lus, indépendamment de leur extension, pour obtenir les informations nécessaires permettant à [PROJ4](http://proj4js.org/) d'effectuer des reprojections. +Le fichier *service.json* indique un dossier de projections. Ce dossier peut contenir plusieurs fichiers JSON. Ces fichiers seront lus, indépendamment de leur extension, pour obtenir les informations nécessaires permettant à [PROJ4](http://proj4js.org/) d'effectuer des reprojections. -On peut trouver un [exemple](../../docker/config/projections/projection.json) de ce fichier et le [modèle](./projection_model.yaml) au format YAML. +On peut trouver un [exemple](../../docker/config/projections/projection.json) de ce fichier et le [modèle](./projections/projection_model.yaml) au format YAML. -## Les ressources +## Les sources -Dans le fichier *server.json*, il est possible d'indiquer plusieurs dossiers *resources*. Chaque dossier sera lu et les fichiers `*.resource` seront analysés par Road2. Chacun de ces fichiers représente une ressource pour Road2. +Dans les fichiers du type *service.json*, il est possible d'indiquer plusieurs dossiers *sources*. Chaque dossier sera lu et les fichiers `*.source` seront analysés par Road2. Chacun de ces fichiers représente une source pour Road2. -On peut trouver un [exemple](../../docker/config/resources/corse.resource) de ce fichier et le [modèle](./resource_model_osrm.yaml) au format YAML pour OSRM. Pour PGRouting, il y a également un [exemple](./bduni_idf_pgr.resource) et un [modèle](./resource_model_pgr.yaml). +On peut trouver, dans ce [dossier](./sources/), un exemple de ce genre de fichier pour chaque type de source disponible dans le code de Road2. -### Les lua et les json des sources +## Les ressources -Chaque source d'une ressource est rattaché à un profile et une optimisation. Cela détermine un coût pour chaque tronçon du graphe. Pour calculer ces coût, nous utilisons un fichier spécifique qui contient les règles de passage des attributs d'un tronçon à son coût. Ce fichier est un json géré dans le projet route-graph-generator. Cer dernier contient donc au moins un exemple. -À partir de ce json, un lua est créé pour OSRM. Ce fichier lua est aussi dans le projet route-graph-generator. +Dans les fichiers *service.json*, il peut également indiquer plusieurs dossiers *resources*. Chaque dossier sera lu et les fichiers `*.resource` seront analysés par Road2. Chacun de ces fichiers représente cette fois-ci une ressource pour Road2. -Ces deux fichiers ne sont pas obligatoires dans la configuration mais ils sont fournis pour que l'on puisse retrouver les informations de création des graphes. Il est donc utile de les avoir. Ils devraient être fournis par route-graph-generator lors d'une génération. +On peut trouver, dans ce [dossier](./resources/), un exemple de ce genre de fichier pour chaque type de ressource disponible dans le code de Road2. Chaque type suit le même modèle YAML. ## Les fichiers liés à certains moteurs de Road2 ### PGRouting: La configuration d'une base de données -Afin de lire les données dans un base, il est nécessaire de fournir à Road2 un fichier qui lui donne les identifiants de connexion à la base. Cela est possible via un fichier json. Un exemple de ce fichier est fourni [ici](./configuration_bdd.json). Le contenu de ce fichier correspond aux options du module NodeJS `pg`. \ No newline at end of file +Afin de lire les données dans un base, il est nécessaire de fournir à Road2 un fichier qui lui donne les identifiants de connexion à la base. Cela est possible via un fichier json. Un exemple de ce fichier est fourni [ici](./pgrouting/configuration_bdd.json). Le contenu de ce fichier correspond aux options du module NodeJS `pg`. \ No newline at end of file diff --git a/documentation/configuration/resource_model_osrm.yaml b/documentation/configuration/resource_model_osrm.yaml deleted file mode 100644 index e26d7ab..0000000 --- a/documentation/configuration/resource_model_osrm.yaml +++ /dev/null @@ -1,146 +0,0 @@ -# Description d'un fichier des ressources OSRM pour Road2 - - -# Information sur la ressource générée. La plupart de ces informations permettront de générer le fichier de ressource utilisable par l'application de calcul d'itinéraire. -"resource": - type: object - required: true - properties: - # Id de la ressource - "id": - type: string - required: true - # Type de la ressource, osrm dans ce cas - "type": - type: string - required: true - # Description de la ressource - "description": - type: string - required: true - # Version de la ressource. C'est généralement la date de génération de cette ressource. - "resourceVersion" - type: "string" - required: true - # Informations sur la topologie de la ressource - "topology": - type: object - required: true - properties: - # id de la topologie, utile pour la charger une seule fois dans Road2 - "id": - type: string - required: true - # Type de la topologie - "type": - type: string - required: true - # Description de la topologie - "description": - type: string - required: true - # Stockage de la topologie - "storage": - $ref: "#/components/schemas/storage" - required: true - # Projection des données sources - "projection": - type: string - required: true - # Bbox des données de la topologie, pas nécessairement la même que pour la ressource - "bbox": - type: string - required: true - # Liste des sources qui vont être générées - "sources": - type: array - required: true - minItems: 1 - items: - type: object - properties: - # id de la source, utile pour la charger une seule fois dans Road2. - "id": - type: string - required: true - # Type de la source, ici ce doit être osrm - "type": - type: string - required: true - # Stockage de la source, du .osrm en l'occurence - "storage": - $ref: "#/components/schemas/storage" - required: true - # Coûts calculés sur la topologie précisée précedemment. On peut en avoir plusieurs. - "cost": - type: object - required: true - properties: - # Chaque coût correspond à un profil - "profile": - type: string - required: true - # Chaque coût correspond à un profil - "optmization": - type: string - required: true - # Fichier permettant de calculer le coût - "compute": - type: object - required: false - properties: - # Stockage du fichier permettant de calculer le coût - "storage": - $ref: "#/components/schemas/storage" - # Configuration pour le calcul des couts - "configuration": - type: object - required: true - properties: - # Nom du coût dans le fichier de configuration - "name": - type: string - required: true - # Stockage du fichier de calcul des couts. C'est un JSON. - "storage": - $ref: "#/components/schemas/storage" - # Informations sur les opérations autorisées sur la ressource qui va être générée. - "availableOperations": - type: array - required: true - minItems: 1 - items: - # Id de l'opération - "id": - type: string - required: true - # Paramètres de l'opération - "parameters": - type: array - required: true - items: - type: object - properties: - # Id du parametre - "id": - type: string - required: true - # Valeur par défaut possible pour ce parametre - "defaultValueContent": - type: string - required: false - # Valeurs possibles pour ce parametre - "values": - type: string or array or object - required: true - -components: - schemas: - # storage d'un document: fichier ou ceph - "storage": - type: object - properties: - # Fichier - "file": - type: string - required: false \ No newline at end of file diff --git a/documentation/configuration/resource_model_pgr.yaml b/documentation/configuration/resource_model_pgr.yaml deleted file mode 100644 index f95f8c1..0000000 --- a/documentation/configuration/resource_model_pgr.yaml +++ /dev/null @@ -1,190 +0,0 @@ -# Description d'un fichier de configuration de génération pour des ressources OSRM - -# Information sur la ressource générée. La plupart de ces informations permettront de générer le fichier de ressource utilisable par l'application de calcul d'itinéraire. -"resource": - type: object - required: true - properties: - # Id de la ressource - "id": - type: string - required: true - # Type de la ressource, pge dans ce cas - "type": - type: string - required: true - # Description de la ressource - "description": - type: string - required: true - # Version de la ressource. C'est généralement la date de génération de cette ressource. - "resourceVersion" - type: "string" - required: true - # Informations sur la topologie de la ressource - "topology": - type: object - required: true - properties: - # id de la topologie, utile pour la charger une seule fois dans Road2 - "id": - type: string - required: true - # Type de la topologie - "type": - type: string - required: true - # Description de la topologie - "description": - type: string - required: true - # Stockage de la topologie - "storage": - type: object - required: true - properties: - # Information sur la base qui contient la topologie - "base": - type: object - required: true - properties: - # Fichier de configuration pour se connecter à la base de données, utile que pour Road2 - "dbConfig": - type: string - required: true - # Schéma dans la base de données - "schema": - type: string - required: true - # Liste des attributs de le topologie disponibles dans la réponse - "attributes": - type: array - required: false - items: - type: object - properties: - # Clé utilisée par le client de l'api - "key": - type: string - required: true - # Nom de la colonne dans la base de donnés - "column": - type: string - required: true - # Indique si l'attribut est présent par défaut dans la réponse, "true" ou "false" - "default": - type: string - required: true - # Projection des données sources - "projection": - type: string - required: true - # Bbox des données de la topologie, pas nécessairement la même que pour la ressource - "bbox": - type: string - required: true - # Liste des sources qui vont être générées - "sources": - type: array - required: true - minItems: 1 - items: - type: object - properties: - # id de la source, utile pour la charger une seule fois dans Road2. - "id": - type: string - required: true - # Type de la source, ici ce doit être osrm - "type": - type: string - required: true - # Stockage de la source, du .osrm en l'occurence - "storage": - type: object - required: true - properties: - # Fichier de configuration de la base de données - "dbConfig": - type: string - required: true - # Colonne des coûts - "costColumn": - type: string - required: true - # Colonne des coûts inverse - "rcostColumn": - type: string - required: true - # Coûts qui seront calculés sur la topologie précisée précedemment. On peut en avoir plusieurs. - "cost": - type: object - required: true - properties: - # Chaque coût correspond à un profil - "profile": - type: string - required: true - # Chaque coût correspond à un profil - "optmization": - type: string - required: true - # Fichier permettant de calculer le coût - "compute": - type: object - required: true - properties: - # Stockage du fichier permettant de calculer le coût - "storage": - $ref: "#/components/schemas/storage" - # Configuration pour le calcul des couts - "configuration": - type: object - required: false - properties: - # Nom du coût dans le fichier de configuration - "name": - type: string - required: true - # Stockage du fichier de calcul des couts. C'est un JSON. - "storage": - $ref: "#/components/schemas/storage" - # Informations sur les opérations autorisées sur la ressource qui va être générée. - "availableOperations": - type: array - required: true - minItems: 1 - items: - # Id de l'opération - "id": - type: string - required: true - # Paramètres de l'opération - "parameters": - type: array - required: true - items: - type: object - properties: - # Id du parametre - "id": - type: string - required: true - # Valeur par défaut possible pour ce parametre - "defaultValueContent": - type: string - required: false - # Valeurs possibles pour ce parametre - "values": - type: string or array or object - required: true -components: - schemas: - # storage d'un document: fichier ou ceph - "storage": - type: object - properties: - # Fichier - "file": - type: string - required: false \ No newline at end of file diff --git a/documentation/configuration/corse.resource b/documentation/configuration/resources/osrm.resource similarity index 80% rename from documentation/configuration/corse.resource rename to documentation/configuration/resources/osrm.resource index 555ee9a..a92cd82 100644 --- a/documentation/configuration/corse.resource +++ b/documentation/configuration/resources/osrm.resource @@ -1,36 +1,11 @@ { "resource": { - "id": "corse-osm", + "id": "data-osm", "type": "osrm", - "description": "Exemple d'une ressource sur la Corse avec les données OSM.", - "resourceVersion": "2020-09-28", - "topology": { - "id": "corse-osm", - "type": "osm", - "description": "Données OSM sur la Corse.", - "storage": { - "file": "/home/docker/internal/corse-latest.osm.pbf" - }, - "projection": "EPSG:4326", - "bbox": "-90,-180,90,180" - }, + "description": "Exemple d'une ressource créée à partir des données OSM.", + "resourceVersion": "yyyy-mm-dd", "sources": [ - { - "id": "corse-car-fastest", - "type": "osrm", - "storage": { - "file": "/home/docker/internal/corse-latest.osrm" - }, - "cost": { - "profile": "car", - "optimization": "fastest", - "compute": { - "storage": { - "file": "/usr/local/share/osrm/profiles/car.lua" - } - } - } - } + "data-osm-car-fastest" ], "availableOperations":[ { @@ -39,7 +14,7 @@ { "id": "resource", "values": [ - "corse-osm" + "data-osm" ] }, { @@ -157,7 +132,7 @@ { "id": "resource", "values": [ - "corse-osm" + "data-osm" ] }, { @@ -169,6 +144,7 @@ }, { "id": "number", + "defaultValueContent": 1, "values": { "min": 1, "max": 10 diff --git a/documentation/configuration/bduni_idf_pgr.resource b/documentation/configuration/resources/pgr.resource similarity index 71% rename from documentation/configuration/bduni_idf_pgr.resource rename to documentation/configuration/resources/pgr.resource index ef058a3..71d25ab 100644 --- a/documentation/configuration/bduni_idf_pgr.resource +++ b/documentation/configuration/resources/pgr.resource @@ -2,271 +2,10 @@ "resource": { "id": "bduni-idf-pgr", "type": "pgr", - "description": "Donn\u00e9es BDUNI v2.", - "resourceVersion": "10/05/2020", - "topology": { - "id": "base-bduni", - "type": "db", - "description": "Donn\u00e9es issues de la BDUNI de l'IGN.", - "storage": { - "base": { - "dbConfig": "/home/docker/data/output_base.json", - "schema": "public", - "attributes": [ - { - "key": "name", - "column": "way_names", - "default": "false" - }, - { - "key": "nom_1_gauche", - "column": "nom_1_gauche", - "default": "true" - }, - { - "key": "nom_1_droite", - "column": "nom_1_droite", - "default": "true" - }, - { - "key": "cpx_numero", - "column": "cpx_numero", - "default": "true" - }, - { - "key": "cpx_toponyme_route_nommee", - "column": "cpx_toponyme_route_nommee", - "default": "true" - }, - { - "key": "cleabs", - "column": "cleabs", - "default": "false" - }, - { - "key": "nature", - "column": "nature", - "default": "false" - }, - { - "key": "importance", - "column": "importance", - "default": "false" - }, - { - "key": "position_par_rapport_au_sol", - "column": "position_par_rapport_au_sol", - "default": "false" - }, - { - "key": "nombre_de_voies", - "column": "nombre_de_voies", - "default": "false" - }, - { - "key": "largeur_de_chaussee", - "column": "largeur_de_chaussee", - "default": "false" - }, - { - "key": "itineraire_vert", - "column": "itineraire_vert", - "default": "false" - }, - { - "key": "sens_de_circulation", - "column": "sens_de_circulation", - "default": "false" - }, - { - "key": "bande_cyclable", - "column": "bande_cyclable", - "default": "false" - }, - { - "key": "reserve_aux_bus", - "column": "reserve_aux_bus", - "default": "false" - }, - { - "key": "urbain", - "column": "urbain", - "default": "false" - }, - { - "key": "vitesse_moyenne_vl", - "column": "vitesse_moyenne_vl", - "default": "false" - }, - { - "key": "acces_vehicule_leger", - "column": "acces_vehicule_leger", - "default": "false" - }, - { - "key": "acces_pieton", - "column": "acces_pieton", - "default": "false" - }, - { - "key": "nature_de_la_restriction", - "column": "nature_de_la_restriction", - "default": "false" - }, - { - "key": "restriction_de_hauteur", - "column": "restriction_de_hauteur", - "default": "false" - }, - { - "key": "restriction_de_poids_total", - "column": "restriction_de_poids_total", - "default": "false" - }, - { - "key": "restriction_de_poids_par_essieu", - "column": "restriction_de_poids_par_essieu", - "default": "false" - }, - { - "key": "restriction_de_largeur", - "column": "restriction_de_largeur", - "default": "false" - }, - { - "key": "restriction_de_longueur", - "column": "restriction_de_longueur", - "default": "false" - }, - { - "key": "matieres_dangereuses_interdites", - "column": "matieres_dangereuses_interdites", - "default": "false" - }, - { - "key": "insee_commune_gauche", - "column": "insee_commune_gauche", - "default": "false" - }, - { - "key": "insee_commune_droite", - "column": "insee_commune_droite", - "default": "false" - }, - { - "key": "cpx_numero_route_europeenne", - "column": "cpx_numero_route_europeenne", - "default": "false" - }, - { - "key": "cpx_classement_administratif", - "column": "cpx_classement_administratif", - "default": "false" - }, - { - "key": "cpx_gestionnaire", - "column": "cpx_gestionnaire", - "default": "false" - } - ] - } - }, - "projection": "EPSG:4326", - "bbox": "1.7,48.4,3.3,49.1" - }, + "description": "Données BDUNI v2.", + "resourceVersion": "yyyy-mm-dd", "sources": [ - { - "id": "bduni-idf-car-fastest-pgr", - "type": "pgr", - "storage": { - "costColumn": "cost_s_car", - "rcostColumn": "reverse_cost_s_car" - }, - "cost": { - "profile": "car", - "optimization": "fastest", - "compute": { - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - }, - "configuration": { - "name": "cost_s_car", - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - } - } - } - } - }, - { - "id": "bduni-idf-car-shortest-pgr", - "type": "pgr", - "storage": { - "costColumn": "cost_m_car", - "rcostColumn": "reverse_cost_m_car" - }, - "cost": { - "profile": "car", - "optimization": "shortest", - "compute": { - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - }, - "configuration": { - "name": "cost_m_car", - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - } - } - } - } - }, - { - "id": "bduni-idf-pedestrian-fastest-pgr", - "type": "pgr", - "storage": { - "costColumn": "cost_s_pedestrian", - "rcostColumn": "reverse_cost_s_pedestrian" - }, - "cost": { - "profile": "pedestrian", - "optimization": "fastest", - "compute": { - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - }, - "configuration": { - "name": "cost_s_pedestrian", - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - } - } - } - } - }, - { - "id": "bduni-idf-pedestrian-shortest-pgr", - "type": "pgr", - "storage": { - "costColumn": "cost_m_pedestrian", - "rcostColumn": "reverse_cost_m_pedestrian" - }, - "cost": { - "profile": "pedestrian", - "optimization": "shortest", - "compute": { - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - }, - "configuration": { - "name": "cost_m_pedestrian", - "storage": { - "file": "/home/docker/data/costs_calculation_sample.json" - } - } - } - } - } + "bduni-idf-pgr" ], "availableOperations": [ { @@ -433,7 +172,7 @@ }, { "keyType": "numerical-pgr", - "key": "LARGEUR", + "key": "largeur", "availableConstraintType": [ "banned" ], @@ -441,15 +180,17 @@ }, { "keyType": "numerical-pgr", - "key": "IMPORTANCE", + "key": "importance", "availableConstraintType": [ - "banned" + "banned", + "avoid", + "prefer" ], "field": "importance" }, { "keyType": "name-pgr", - "key": "NATURE", + "key": "nature", "availableConstraintType": [ "banned" ], @@ -546,9 +287,11 @@ }, { "keyType": "name-pgr", - "key": "CL_ADMIN", + "key": "cpx_classement_administratif", "availableConstraintType": [ - "banned" + "banned", + "prefer", + "avoid" ], "availableValues": [ { @@ -630,7 +373,11 @@ ] }, { - "id": "costValue" + "id": "costValue", + "values": { + "min": 100, + "max": 20000 + } }, { "id": "profile", @@ -923,4 +670,3 @@ } ] } -} \ No newline at end of file diff --git a/documentation/configuration/resources/resource_model.yaml b/documentation/configuration/resources/resource_model.yaml new file mode 100644 index 0000000..47d5aba --- /dev/null +++ b/documentation/configuration/resources/resource_model.yaml @@ -0,0 +1,64 @@ +# Description d'un fichier de ressource pour Road2 + +# Information sur la ressource diffusée. +"resource": + type: object + required: true + properties: + # Id de la ressource, unique par instance de Road2 + "id": + type: string + required: true + # Type de la ressource + "type": + type: string + required: true + enum: ["osrm","pgr","smartpgr","valhalla"] + # Description de la ressource + "description": + type: string + required: true + # Version de la ressource. C'est généralement la date de génération de cette ressource. + "resourceVersion" + type: "string" + required: true + # Liste des sources proposée dans cette ressource. On indique seulement les ids des sources. + # Il y a une correspondance qui doit être respectée entre les types de source et de ressource + "sources": + type: array + required: true + minItems: 1 + items: + type: string + # Informations sur les opérations autorisées sur la ressource qui va être générée. + "availableOperations": + type: array + required: true + minItems: 1 + items: + type: object + properties: + # Id de l'opération + "id": + type: string + required: true + # Paramètres de l'opération + "parameters": + type: array + required: true + items: + type: object + properties: + # Id du parametre + "id": + type: string + required: true + # Valeur par défaut possible pour ce parametre + "defaultValueContent": + type: string + required: false + # Valeurs possibles pour ce parametre + # TODO : finir de spécifier (en attendant, voir les exemples de fichier) + "values": + type: string or array or object + required: true diff --git a/documentation/configuration/resources/smartpgr.resource b/documentation/configuration/resources/smartpgr.resource new file mode 100644 index 0000000..5d610ed --- /dev/null +++ b/documentation/configuration/resources/smartpgr.resource @@ -0,0 +1,673 @@ +{ + "resource": { + "id": "bduni-idf-smartpgr", + "type": "smartpgr", + "resourceVersion": "yyyy-mm-dd", + "threshold": 30000, + "description": "Donn\u00e9es BDUNI v2.", + "sources": [ + "bdtopo-smartrouting", + "bduni-idf-pgr" + ], + "availableOperations": [ + { + "id": "route", + "parameters": [ + { + "id": "resource", + "values": [ + "bduni-idf-smartpgr" + ] + }, + { + "id": "start", + "values": { + "bbox": "1.7,48.4,3.3,49.1", + "projection": "EPSG:4326" + } + }, + { + "id": "end", + "values": { + "bbox": "1.7,48.4,3.3,49.1", + "projection": "EPSG:4326" + } + }, + { + "id": "profile", + "defaultValueContent": "car", + "values": [ + "car", + "pedestrian" + ] + }, + { + "id": "optimization", + "defaultValueContent": "fastest", + "values": [ + "fastest", + "shortest" + ] + }, + { + "id": "intermediates", + "values": { + "bbox": "1.7,48.4,3.3,49.1", + "projection": "EPSG:4326" + } + }, + { + "id": "getSteps", + "defaultValueContent": "true" + }, + { + "id": "waysAttributes", + "values": [ + "name", + "nom_1_gauche", + "nom_1_droite", + "cpx_numero", + "cpx_toponyme_route_nommee", + "cleabs", + "nature", + "importance", + "position_par_rapport_au_sol", + "nombre_de_voies", + "largeur_de_chaussee", + "itineraire_vert", + "sens_de_circulation", + "bande_cyclable", + "reserve_aux_bus", + "urbain", + "vitesse_moyenne_vl", + "acces_vehicule_leger", + "acces_pieton", + "nature_de_la_restriction", + "restriction_de_hauteur", + "restriction_de_poids_total", + "restriction_de_poids_par_essieu", + "restriction_de_largeur", + "restriction_de_longueur", + "matieres_dangereuses_interdites", + "insee_commune_gauche", + "insee_commune_droite", + "cpx_numero_route_europeenne", + "cpx_classement_administratif", + "cpx_gestionnaire" + ] + }, + { + "id": "geometryFormat", + "defaultValueContent": "geojson", + "values": [ + "geojson", + "polyline" + ] + }, + { + "id": "bbox", + "defaultValueContent": "true" + }, + { + "id": "projection", + "defaultValueContent": "EPSG:4326", + "values": [ + "EPSG:4326", + "EPSG:2154" + ] + }, + { + "id": "timeUnit", + "defaultValueContent": "minute", + "values": [ + "hour", + "minute", + "second", + "standard" + ] + }, + { + "id": "distanceUnit", + "defaultValueContent": "meter", + "values": [ + "meter", + "kilometer" + ] + }, + { + "id": "constraints", + "defaultPreferredCostRatio": 0.8, + "defaultAvoidCostRatio": 1.2, + "values": [ + { + "keyType": "name-pgr", + "key": "wayType", + "availableConstraintType": [ + "banned" + ], + "availableValues": [ + { + "value": "autoroute", + "field": "acces_vehicule_leger", + "condition": { + "type": "equal", + "value": "$niv4$A p\u00e9age$niv4$" + } + }, + { + "value": "tunnel", + "field": "position_par_rapport_au_sol", + "condition": { + "type": "less", + "value": "0" + } + }, + { + "value": "pont", + "field": "position_par_rapport_au_sol", + "condition": { + "type": "greater", + "value": "0" + } + } + ] + }, + { + "keyType": "numerical-pgr", + "key": "largeur", + "availableConstraintType": [ + "banned" + ], + "field": "largeur_de_chaussee" + }, + { + "keyType": "numerical-pgr", + "key": "importance", + "availableConstraintType": [ + "banned", + "avoid", + "prefer" + ], + "field": "importance" + }, + { + "keyType": "name-pgr", + "key": "nature", + "availableConstraintType": [ + "banned" + ], + "availableValues": [ + { + "value": "sentier", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Sentier$niv4$" + } + }, + { + "value": "bac_ou_liaison_maritime", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Bac ou liaison maritime$niv4$" + } + }, + { + "value": "bretelle", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Bretelle$niv4$" + } + }, + { + "value": "chemin", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Chemin$niv4$" + } + }, + { + "value": "escalier", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Escalier$niv4$" + } + }, + { + "value": "piste_cyclable", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Piste cyclable$niv4$" + } + }, + { + "value": "rond-point", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Rond-point$niv4$" + } + }, + { + "value": "route_a_1_chaussee", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Route \u00e0 1 chauss\u00e9e$niv4$" + } + }, + { + "value": "route_a_2_chaussees", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Route \u00e0 2 chauss\u00e9es$niv4$" + } + }, + { + "value": "route_empierree", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Route empierr\u00e9e$niv4$" + } + }, + { + "value": "type_autoroutier", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Type autoroutier$niv4$" + } + } + ] + }, + { + "keyType": "name-pgr", + "key": "cpx_classement_administratif", + "availableConstraintType": [ + "banned", + "prefer", + "avoid" + ], + "availableValues": [ + { + "value": "vide", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv4$$niv4$" + } + }, + { + "value": "autoroute", + "field": "cpx_classement_administratif", + "condition": { + "type": "like", + "value": "$niv4$%Autoroute%$niv4$" + } + }, + { + "value": "nationale", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv4$Nationale$niv4$" + } + }, + { + "value": "departementale", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv4$D\u00e9partementale$niv4$" + } + }, + { + "value": "voie_communale", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv4$Voie communale$niv4$" + } + }, + { + "value": "chemin_rural", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv4$Chemin rural$niv4$" + } + } + ] + } + ] + } + ] + }, + { + "id": "isochrone", + "parameters": [ + { + "id": "resource", + "values": [ + "bduni-idf-smartpgr" + ] + }, + { + "id": "point", + "values": { + "bbox": "1.7,48.4,3.3,49.1", + "projection": "EPSG:4326" + } + }, + { + "id": "costType", + "defaultValueContent": "time", + "values": [ + "time", + "distance" + ] + }, + { + "id": "costValue", + "values": { + "min": 1 + } + }, + { + "id": "profile", + "defaultValueContent": "car", + "values": [ + "car", + "pedestrian" + ] + }, + { + "id": "direction", + "defaultValueContent": "departure", + "values": [ + "departure", + "arrival" + ] + }, + { + "id": "projection", + "defaultValueContent": "EPSG:4326", + "values": [ + "EPSG:4326", + "EPSG:2154", + "EPSG:4559", + "EPSG:2972", + "EPSG:2975", + "EPSG:4471", + "EPSG:3857" + ] + }, + { + "id": "geometryFormat", + "defaultValueContent": "geojson", + "values": [ + "geojson", + "polyline" + ] + }, + { + "id": "timeUnit", + "defaultValueContent": "second", + "values": [ + "hour", + "minute", + "second" + ] + }, + { + "id": "distanceUnit", + "defaultValueContent": "meter", + "values": [ + "meter", + "kilometer" + ] + }, + { + "id": "constraints", + "values": [ + { + "keyType": "name-pgr", + "key": "wayType", + "availableConstraintType": [ + "banned" + ], + "availableValues": [ + { + "value": "autoroute", + "field": "acces_vehicule_leger", + "condition": { + "type": "equal", + "value": "$niv3$A p\u00e9age$niv3$" + } + }, + { + "value": "tunnel", + "field": "position_par_rapport_au_sol", + "condition": { + "type": "less", + "value": "0" + } + }, + { + "value": "pont", + "field": "position_par_rapport_au_sol", + "condition": { + "type": "greater", + "value": "0" + } + } + ] + }, + { + "keyType": "numerical-pgr", + "key": "LARGEUR", + "availableConstraintType": [ + "banned" + ], + "field": "largeur_de_chaussee" + }, + { + "keyType": "numerical-pgr", + "key": "IMPORTANCE", + "availableConstraintType": [ + "banned" + ], + "field": "importance" + }, + { + "keyType": "name-pgr", + "key": "RESTR_MAT", + "availableConstraintType": [ + "banned" + ], + "availableValues": [ + { + "value": "vrai", + "field": "matieres_dangereuses_interdites", + "condition": { + "type": "equal", + "value": "true" + } + }, + { + "value": "faux", + "field": "matieres_dangereuses_interdites", + "condition": { + "type": "equal", + "value": "false" + } + } + ] + }, + { + "keyType": "name-pgr", + "key": "NATURE", + "availableConstraintType": [ + "banned" + ], + "availableValues": [ + { + "value": "sentier", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Sentier$niv4$" + } + }, + { + "value": "bac_ou_liaison_maritime", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Bac ou liaison maritime$niv4$" + } + }, + { + "value": "bretelle", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Bretelle$niv4$" + } + }, + { + "value": "chemin", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Chemin$niv4$" + } + }, + { + "value": "escalier", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Escalier$niv4$" + } + }, + { + "value": "piste_cyclable", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Piste cyclable$niv4$" + } + }, + { + "value": "rond-point", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Rond-point$niv4$" + } + }, + { + "value": "route_a_1_chaussee", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Route \u00e0 1 chauss\u00e9e$niv4$" + } + }, + { + "value": "route_a_2_chaussees", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Route \u00e0 2 chauss\u00e9es$niv4$" + } + }, + { + "value": "route_empierree", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Route empierr\u00e9e$niv4$" + } + }, + { + "value": "type_autoroutier", + "field": "nature", + "condition": { + "type": "equal", + "value": "$niv4$Type autoroutier$niv4$" + } + } + ] + }, + { + "keyType": "name-pgr", + "key": "CL_ADMIN", + "availableConstraintType": [ + "banned" + ], + "availableValues": [ + { + "value": "vide", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv4$$niv4$" + } + }, + { + "value": "autoroute", + "field": "cpx_classement_administratif", + "condition": { + "type": "like", + "value": "$niv3$%Autoroute%$niv3$" + } + }, + { + "value": "nationale", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv3$Nationale$niv3$" + } + }, + { + "value": "departementale", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv3$D\u00e9partementale$niv3$" + } + }, + { + "value": "voie_communale", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv3$Voie communale$niv3$" + } + }, + { + "value": "chemin_rural", + "field": "cpx_classement_administratif", + "condition": { + "type": "equal", + "value": "$niv3$Chemin rural$niv3$" + } + } + ] + } + ] + } + ] + } + ] + } diff --git a/documentation/configuration/resources/valhalla.resource b/documentation/configuration/resources/valhalla.resource new file mode 100644 index 0000000..ee66b77 --- /dev/null +++ b/documentation/configuration/resources/valhalla.resource @@ -0,0 +1,229 @@ +"resource": { + "id": "bdtopo-valhalla", + "type": "valhalla", + "description": "Données BDUNI v2.", + "sources": [ + "bdtopo-auto-valhalla", + ], + "availableOperations":[ + { + "id": "route", + "parameters": [ + { + "id": "resource", + "values": [ + "bdtopo-valhalla" + ] + }, + { + "id": "start", + "values": { + "bbox": "-180,-90,180,90", + "projection": "EPSG:4326" + } + }, + { + "id": "end", + "values": { + "bbox": "-180,-90,180,90", + "projection": "EPSG:4326" + } + }, + { + "id": "profile", + "defaultValueContent": "car", + "values": [ + "car", + "pedestrian" + ] + }, + { + "id": "optimization", + "defaultValueContent": "fastest", + "values": [ + "fastest", + "shortest" + ] + }, + { + "id": "intermediates", + "values": { + "bbox": "-180,-90,180,90", + "projection": "EPSG:4326" + } + }, + { + "id": "getSteps", + "defaultValueContent": "true" + }, + { + "id": "waysAttributes", + "values": [ + "name" + ] + }, + { + "id": "geometryFormat", + "defaultValueContent": "geojson", + "values": [ + "geojson", + "polyline" + ] + }, + { + "id": "bbox", + "defaultValueContent": "true" + }, + { + "id": "projection", + "defaultValueContent": "EPSG:4326", + "values": [ + "EPSG:4326", + "EPSG:2154" + ] + }, + { + "id": "timeUnit", + "defaultValueContent": "minute", + "values": [ + "hour", + "minute", + "second", + "standard" + ] + }, + { + "id": "distanceUnit", + "defaultValueContent": "meter", + "values": [ + "meter", + "kilometer" + ] + }, + { + "id": "constraints", + "values": [ + { + "keyType": "name-valhalla", + "key": "wayType", + "availableConstraintType": ["banned"], + "availableValues": [ + { + "value": "autoroute", + "field": "toll" + } + ] + } + ] + } + ] + }, + { + "id": "isochrone", + "parameters": [ + { + "id": "resource", + "values": [ + "bdtopo-pgr" + ] + }, + { + "id": "point", + "values": { + "bbox": "-180,-90,180,90", + "projection": "EPSG:4326" + } + }, + { + "id": "costType", + "defaultValueContent": "time", + "values": [ + "time", + "distance" + ] + }, + { + "id": "costValue", + "values": { + "min": 100, + "max": 50000 + } + }, + { + "id": "profile", + "defaultValueContent": "car", + "values": [ + "car", + "pedestrian" + ] + }, + { + "id": "direction", + "defaultValueContent": "departure", + "values": [ + "departure", + "arrival" + ] + }, + { + "id": "constraints", + "values": [ + { + "keyType": "name-pgr", + "key": "wayType", + "availableConstraintType": ["banned"], + "availableValues": [ + { + "value": "autoroute", + "field": "acces_vehicule_leger", + "condition": { + "type": "equal", + "value": "$niv3$A péage$niv3$" + } + } + ] + } + ] + }, + { + "id": "projection", + "defaultValueContent": "EPSG:4326", + "values": [ + "EPSG:4326", + "EPSG:2154", + "EPSG:4559", + "EPSG:2972", + "EPSG:2975", + "EPSG:4471", + "EPSG:3857" + ] + }, + { + "id": "geometryFormat", + "defaultValueContent": "geojson", + "values": [ + "geojson", + "polyline" + ] + }, + { + "id": "timeUnit", + "defaultValueContent": "second", + "values": [ + "hour", + "minute", + "second" + ] + }, + { + "id": "distanceUnit", + "defaultValueContent": "meter", + "values": [ + "meter", + "kilometer" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/documentation/configuration/service_model.yaml b/documentation/configuration/services/service_model.yaml similarity index 100% rename from documentation/configuration/service_model.yaml rename to documentation/configuration/services/service_model.yaml diff --git a/documentation/configuration/sources/osrm.source b/documentation/configuration/sources/osrm.source new file mode 100644 index 0000000..b4d128b --- /dev/null +++ b/documentation/configuration/sources/osrm.source @@ -0,0 +1,14 @@ +{ + "id": "bduni-idf-car-fastest", + "description":"test osrm", + "type": "osrm", + "projection": "EPSG:4326", + "bbox": "1.7,48.4,3.3,49.1", + "storage": { + "file": "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" + }, + "cost": { + "profile": "car", + "optimization": "fastest" + } +} diff --git a/documentation/configuration/sources/pgr.source b/documentation/configuration/sources/pgr.source new file mode 100644 index 0000000..4c726e4 --- /dev/null +++ b/documentation/configuration/sources/pgr.source @@ -0,0 +1,200 @@ +{ + "id": "bduni-idf-car-fastest-pgr", + "description":"test pgr", + "type": "pgr", + "projection": "EPSG:4326", + "bbox": "1.7,48.4,3.3,49.1", + "storage": { + "base": { + "dbConfig": "/home/docker/data/output_base.json", + "schema": "public", + "attributes": [ + { + "key": "name", + "column": "way_names", + "default": "false" + }, + { + "key": "nom_1_gauche", + "column": "nom_1_gauche", + "default": "true" + }, + { + "key": "nom_1_droite", + "column": "nom_1_droite", + "default": "true" + }, + { + "key": "cpx_numero", + "column": "cpx_numero", + "default": "true" + }, + { + "key": "cpx_toponyme_route_nommee", + "column": "cpx_toponyme_route_nommee", + "default": "true" + }, + { + "key": "cleabs", + "column": "cleabs", + "default": "false" + }, + { + "key": "nature", + "column": "nature", + "default": "false" + }, + { + "key": "importance", + "column": "importance", + "default": "false" + }, + { + "key": "position_par_rapport_au_sol", + "column": "position_par_rapport_au_sol", + "default": "false" + }, + { + "key": "nombre_de_voies", + "column": "nombre_de_voies", + "default": "false" + }, + { + "key": "largeur_de_chaussee", + "column": "largeur_de_chaussee", + "default": "false" + }, + { + "key": "itineraire_vert", + "column": "itineraire_vert", + "default": "false" + }, + { + "key": "sens_de_circulation", + "column": "sens_de_circulation", + "default": "false" + }, + { + "key": "bande_cyclable", + "column": "bande_cyclable", + "default": "false" + }, + { + "key": "reserve_aux_bus", + "column": "reserve_aux_bus", + "default": "false" + }, + { + "key": "urbain", + "column": "urbain", + "default": "false" + }, + { + "key": "vitesse_moyenne_vl", + "column": "vitesse_moyenne_vl", + "default": "false" + }, + { + "key": "acces_vehicule_leger", + "column": "acces_vehicule_leger", + "default": "false" + }, + { + "key": "acces_pieton", + "column": "acces_pieton", + "default": "false" + }, + { + "key": "nature_de_la_restriction", + "column": "nature_de_la_restriction", + "default": "false" + }, + { + "key": "restriction_de_hauteur", + "column": "restriction_de_hauteur", + "default": "false" + }, + { + "key": "restriction_de_poids_total", + "column": "restriction_de_poids_total", + "default": "false" + }, + { + "key": "restriction_de_poids_par_essieu", + "column": "restriction_de_poids_par_essieu", + "default": "false" + }, + { + "key": "restriction_de_largeur", + "column": "restriction_de_largeur", + "default": "false" + }, + { + "key": "restriction_de_longueur", + "column": "restriction_de_longueur", + "default": "false" + }, + { + "key": "matieres_dangereuses_interdites", + "column": "matieres_dangereuses_interdites", + "default": "false" + }, + { + "key": "insee_commune_gauche", + "column": "insee_commune_gauche", + "default": "false" + }, + { + "key": "insee_commune_droite", + "column": "insee_commune_droite", + "default": "false" + }, + { + "key": "cpx_numero_route_europeenne", + "column": "cpx_numero_route_europeenne", + "default": "false" + }, + { + "key": "cpx_classement_administratif", + "column": "cpx_classement_administratif", + "default": "false" + }, + { + "key": "cpx_gestionnaire", + "column": "cpx_gestionnaire", + "default": "false" + } + ] + } + }, + "costs": [ + { + "profile": "car", + "optimization": "fastest", + "costType": "time", + "costColumn": "cost_s_car", + "rcostColumn": "reverse_cost_s_car" + }, + { + "profile": "car", + "optimization": "shortest", + "costType": "distance", + "costColumn": "cost_m_car", + "rcostColumn": "reverse_cost_m_car" + }, + { + "profile": "pedestrian", + "optimization": "fastest", + "costType": "time", + "costColumn": "cost_s_pedestrian", + "rcostColumn": "reverse_cost_s_pedestrian" + }, + { + "profile": "pedestrian", + "optimization": "shortest", + "costType": "distance", + "costColumn": "cost_m_pedestrian", + "rcostColumn": "reverse_cost_m_pedestrian" + } + ] +} diff --git a/documentation/configuration/sources/pgrSource.json b/documentation/configuration/sources/pgrSource.json deleted file mode 100644 index 4a68be5..0000000 --- a/documentation/configuration/sources/pgrSource.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "id": "bduni-idf-car-fastest-pgr", - "description":"test", - "type": "pgr", - "projection": "EPSG:4326", - "bbox": "1.7,48.4,3.3,49.1", - "storage": { - "base": { - "dbConfig": "/home/docker/data/output_base.json", - "schema": "public", - "attributes": [ - { - "key": "name", - "column": "way_names", - "default": "false" - }, - { - "key": "nom_1_gauche", - "column": "nom_1_gauche", - "default": "true" - }, - { - "key": "nom_1_droite", - "column": "nom_1_droite", - "default": "true" - }, - { - "key": "cpx_numero", - "column": "cpx_numero", - "default": "true" - }, - { - "key": "cpx_toponyme_route_nommee", - "column": "cpx_toponyme_route_nommee", - "default": "true" - }, - { - "key": "cleabs", - "column": "cleabs", - "default": "false" - }, - { - "key": "nature", - "column": "nature", - "default": "false" - }, - { - "key": "importance", - "column": "importance", - "default": "false" - }, - { - "key": "position_par_rapport_au_sol", - "column": "position_par_rapport_au_sol", - "default": "false" - }, - { - "key": "nombre_de_voies", - "column": "nombre_de_voies", - "default": "false" - }, - { - "key": "largeur_de_chaussee", - "column": "largeur_de_chaussee", - "default": "false" - }, - { - "key": "itineraire_vert", - "column": "itineraire_vert", - "default": "false" - }, - { - "key": "sens_de_circulation", - "column": "sens_de_circulation", - "default": "false" - }, - { - "key": "bande_cyclable", - "column": "bande_cyclable", - "default": "false" - }, - { - "key": "reserve_aux_bus", - "column": "reserve_aux_bus", - "default": "false" - }, - { - "key": "urbain", - "column": "urbain", - "default": "false" - }, - { - "key": "vitesse_moyenne_vl", - "column": "vitesse_moyenne_vl", - "default": "false" - }, - { - "key": "acces_vehicule_leger", - "column": "acces_vehicule_leger", - "default": "false" - }, - { - "key": "acces_pieton", - "column": "acces_pieton", - "default": "false" - }, - { - "key": "nature_de_la_restriction", - "column": "nature_de_la_restriction", - "default": "false" - }, - { - "key": "restriction_de_hauteur", - "column": "restriction_de_hauteur", - "default": "false" - }, - { - "key": "restriction_de_poids_total", - "column": "restriction_de_poids_total", - "default": "false" - }, - { - "key": "restriction_de_poids_par_essieu", - "column": "restriction_de_poids_par_essieu", - "default": "false" - }, - { - "key": "restriction_de_largeur", - "column": "restriction_de_largeur", - "default": "false" - }, - { - "key": "restriction_de_longueur", - "column": "restriction_de_longueur", - "default": "false" - }, - { - "key": "matieres_dangereuses_interdites", - "column": "matieres_dangereuses_interdites", - "default": "false" - }, - { - "key": "insee_commune_gauche", - "column": "insee_commune_gauche", - "default": "false" - }, - { - "key": "insee_commune_droite", - "column": "insee_commune_droite", - "default": "false" - }, - { - "key": "cpx_numero_route_europeenne", - "column": "cpx_numero_route_europeenne", - "default": "false" - }, - { - "key": "cpx_classement_administratif", - "column": "cpx_classement_administratif", - "default": "false" - }, - { - "key": "cpx_gestionnaire", - "column": "cpx_gestionnaire", - "default": "false" - } - ] - } - }, - "costs": [ - { - "profile": "car", - "optimization": "fastest", - "costColumn": "cost_s_car", - "rcostColumn": "reverse_cost_s_car" - }, - { - "profile": "car", - "optimization": "shortest", - "costColumn": "cost_s_car", - "rcostColumn": "reverse_cost_s_car" - }, - { - "profile": "pedestrian", - "optimization": "fastest", - "costColumn": "cost_s_car", - "rcostColumn": "reverse_cost_s_car" - }, - { - "profile": "pedestrian", - "optimization": "shortest", - "costColumn": "cost_s_car", - "rcostColumn": "reverse_cost_s_car" - } - ] - } diff --git a/documentation/configuration/sources/smartrouting.source b/documentation/configuration/sources/smartrouting.source new file mode 100644 index 0000000..d7c6462 --- /dev/null +++ b/documentation/configuration/sources/smartrouting.source @@ -0,0 +1,10 @@ +{ + "id": "bdtopo-smartrouting", + "type": "smartrouting", + "description":"test", + "projection": "EPSG:4326", + "bbox": "-90,-180,90,180", + "storage": { + "url": "https://wxs.ign.fr/calcul" + } +} diff --git a/documentation/configuration/sources/source_model.yaml b/documentation/configuration/sources/source_model.yaml new file mode 100644 index 0000000..3b4358f --- /dev/null +++ b/documentation/configuration/sources/source_model.yaml @@ -0,0 +1,43 @@ +# Description d'un fichier de source pour Road2 + +# id de la source, utile pour la charger une seule fois dans Road2. +"id": + type: string + required: true +# Description de la source +"description": + type: string + required: true +# Type de la source +"type": + type: string + required: true + enum: ["osrm","pgr","smartrouting","valhalla"] +# Projection des données sources +"projection": + type: string + required: true +# Bbox des données dans la projection précisée +"bbox": + type: string + required: true +# Stockage de la source. Elle dépend de son type +# TODO : finir de spécifier (en attendant, voir les exemples) +"storage": + type: object + required: true +# Coûts calculés sur la topologie précisée précedemment. On peut en avoir plusieurs. +# TODO : finir de spécifier (en attendant, regarder les exemples de fichier) +"cost": + type: object + required: true + properties: + # Chaque coût correspond à un profil + "profile": + type: string + required: true + # Chaque coût correspond à un profil + "optmization": + type: string + required: true + \ No newline at end of file diff --git a/documentation/configuration/sources/valhalla.source b/documentation/configuration/sources/valhalla.source new file mode 100644 index 0000000..62b86e1 --- /dev/null +++ b/documentation/configuration/sources/valhalla.source @@ -0,0 +1,32 @@ +{ + "id": "bdtopo-auto-valhalla", + "description":"test valhalla", + "type": "valhalla", + "projection": "EPSG:4326", + "bbox": "1.7,48.4,3.3,49.1", + "storage": { + "tar": "/home/docker/data/bdtopo-valhalla-tiles.tar", + "dir": "/home/docker/data/bdtopo-valhalla-tiles/", + "config": "/home/docker/data/valhalla.json" + }, + "costs": [ + { + "profile": "car", + "optimization": "fastest", + "costType": "time", + "costing": "auto" + }, + { + "profile": "car", + "optimization": "shortest", + "costType": "distance", + "costing": "auto_shorter" + }, + { + "profile": "pedestrian", + "optimization": "shortest", + "costType": "distance", + "costing": "pedestrian" + } + ] +} \ No newline at end of file diff --git a/documentation/data/readme.md b/documentation/data/readme.md index 25866a7..693262e 100644 --- a/documentation/data/readme.md +++ b/documentation/data/readme.md @@ -39,14 +39,21 @@ Il est possible d'utiliser des données au format d'OSRM. La seule contrainte es ### Données PGRouting -Si des données au format PGRouting sont disponibles, il est possible de les utiliser avec Road2. A priori, il n'y a pas de fortes contraintes sur la version de PGRouting utilisée. La seule contrainte est au niveau logiciel. Il est nécessaire d'installer les procédures du projet GIT `pgrouting-procedures` afin que Road2 puisse requêter la base qui héberge les données et les fonctions de PGRouting. +Si des données au format PGRouting sont disponibles, il est possible de les utiliser avec Road2. A priori, il n'y a pas de fortes contraintes sur la version de PGRouting utilisée. La seule contrainte est au niveau logiciel. Il est nécessaire d'installer les procédures du projet [GIT](https://github.com/IGNF/pgrouting-procedures) `pgrouting-procedures` afin que Road2 puisse requêter la base qui héberge les données et les fonctions de PGRouting. + +### Données Valhalla + +De la même manière, il est possible d'utiliser Road2 avec des données Valhalla. ### Les autres données -Lorsque les données ne sont pas dans l'un des formats gérés par Road2, il reste possible de les convertir. Chacun peut le faire comme il le souhaite pour aboutir à l'un des formats cités précédemment. Cependant, si certains le veulent, un projet GIT a été mis en place, `route-graph-generator`, afin de convertir les données. +Lorsque les données ne sont pas dans l'un des formats gérés par Road2, il reste possible de les convertir. Chacun peut le faire comme il le souhaite pour aboutir à l'un des formats cités précédemment. Cependant, si certains le veulent, un projet [GIT](https://github.com/IGNF/route-graph-generator) a été mis en place, `route-graph-generator`, afin de convertir les données. + +`route-graph-generator` est un ensemble de scripts python qui lisent des données dans différents formats et la convertissent dans un format pivot au sein d'une base de données. À partir de la base pivot, il est possible de convertir les données dans n'importe quel format géré par Road2. Ainsi, une même donnée d'entrée peut permettre des calculs par l'ensemble des moteurs. + +Les formats d'entrée possibles pour les scripts sont une base de données ou le format OSM. -`route-graph-generator` est un ensemble de scripts python qui lisent des données dans une base postgreSQL et la convertissent dans un format pivot. À partir de la base pivot, il est possible de convertir les données dans n'importe quel format géré par Road2. Ainsi, une même donnée d'entrée peut permettre des calculs par l'ensemble des moteurs. -Pour information, la conversion de données quelconques dans le format pivot est effectué par des scripts SQL. Pour le moment, il n'y en a qu'un seul qui permet de convertir des données IGN. Cependant, il est facile d'en ajouter de nouveaux pour partir d'un format différent et d'aboutir au format pivot. À partir de ce format pivot, tout les outils existent déjà pour aboutir aux formats gérés par Road2. +Dans le cas d'une base, la conversion de données dans le format pivot est effectué par des scripts SQL. Pour le moment, il n'y en a qu'un seul qui permet de convertir des données IGN. Cependant, il est facile d'en ajouter de nouveaux pour partir d'un format différent et d'aboutir au format pivot. À partir de ce format pivot, tout les outils existent déjà pour aboutir aux formats gérés par Road2. diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index ee3e3fe..5d04b8b 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -38,15 +38,15 @@ C'est le second concept le plus important après l'indépendance des APIs et des #### 1.2.1 Notion de graphe -Il semble utile de passer la notion de *graphe*, selon Road2, pour expliquer ce qui suit. Quand on fait du calcul d'itinéraire, on utilise un moteur qui lit un *graphe* pour générer l'itinéraire. Or, **un *graphe* est une topologie, c'est-à-dire un ensemble de noeuds et d'arcs qui forment un tout navigable, sur laquelle il y a un unique coût**. +Il semble utile de passer la notion de *graphe*, selon Road2, pour expliquer ce qui suit. Quand on fait du calcul d'itinéraire, on utilise un moteur qui lit un *graphe* pour générer l'itinéraire. Or, **un *graphe* est une topologie, c'est-à-dire un ensemble de noeuds et d'arcs qui forment un tout navigable, sur laquelle il y a au moins un coût**. En effet, à chaque arc est associé au minimum un coût. Ce coût peut être la distance de l'arc ou le temps nécessaire pour le parcourir en voiture. Ainsi, chaque coût peut être vu comme le couple *profile/optimisation*, où *profile* est le moyen de transport (ex. voiture) et *optimisation* est le type de déplacement que l'on souhaite (ex. "plus rapide"). -Certains graphes peuvent avoir plusieurs coûts par topologie (ex. PGRouting) et d'autres non (ex. OSRM). Mais lors d'un calcul d'itinéraire, un seul coût est utilisé. On peut donc considérer que chaque graphe n'a qu'un seul coût. +Certains graphes peuvent avoir plusieurs coûts par topologie (ex. PGRouting, Valhalla) et d'autres non (ex. OSRM). Mais lors d'un calcul d'itinéraire, un seul coût est utilisé. #### 1.2.2 Notion de source -Comme précisé juste au-dessus, pour avoir un itinéraire, il est nécessaire de faire appel à un moteur et à un graphe. La *source*, dans le langage conceptuel de Road2, est l'origine du calcul. **La source contient l'appel à un moteur sur un graphe précis pour obtenir le résultat d'un calcul**. C'est le lien entre l'application et le calcul réel, comme celui d'un itinéraire par exemple. +Comme précisé juste au-dessus, pour avoir un itinéraire, il est nécessaire de faire appel à un moteur qui utilise un graphe. La *source*, dans le langage conceptuel de Road2, est l'origine du calcul. **La source contient l'appel à un moteur sur un graphe précis pour obtenir le résultat d'un calcul**. C'est le lien entre l'application et le calcul réel, comme celui d'un itinéraire par exemple. Concrètement, une *source* regroupe deux entités : - une classe Javascript qui fait le lien entre le reste du code et le moteur. Chaque moteur sera donc lié à Road2 par une classe fille de la classe `Source`. Cette classe fille devra contenir le code qui permet de demander au moteur un itinéraire ou autre chose (ex. isochrone, etc...). C'est ce qui concerne le développeur. @@ -56,19 +56,27 @@ De tout ce qui vient d'être dit, on remarque qu'ajouter un moteur revient à aj De plus, en théorie, une unique source peut faire appel à plusieurs moteurs pour rendre un résultat. L'essentiel est qu'une source ne renvoie qu'un résultat pour une seule requête. +Au final, une source va prendre prendre en compte une instance de `Request`, faire le calcul et renvoyer une instance de `Response`. Cela permet à la source de rester indépendante du reste du code. + +Lorsque l’on fait du calcul d’itinéraire, il faut à minima une topologie et des coûts associés à cette topologie. Un coût correspond à un seul mode de déplacement et une seule optimisation (ex. le couple Voiture/plus court). + +Il se trouve qu'un graphe OSRM ne contient qu’un seul coût par dossier. Il permet donc de calculer des itinéraires uniquement sur un seul mode de déplacement et une seule optimisation. Par contre, PgRouting propose autant de colonnes de coût que l'on souhaite sur une même topologie. On retrouve le même regroupement de couples sur une topologie dans Valhalla. + #### 1.2.3 Notion de ressource -Il est une contrainte technique qu’il serait préférable de masquer à l’utilisateur. Lorsque l’on fait du calcul d’itinéraire, il faut à minima une topologie et des coûts associés à cette topologie. Un coût correspond à un seul mode de déplacement et une seule optimisation(ex. Voiture/plus court). +Cependant, pour l'utilisateur et pour l'administrateur du service, nous avons créé la notion de *ressource*. **Une ressource sera définie comme un ensemble de sources**. C'est elle qui fait le lien entre une requête et la bonne source permettant d'y répondre. + +À l'origine, l'objectif était de pouvoir associer plusieurs sources issues des mêmes données mais ayant un calcul de coût différent, et donc de donner à l’*utilisateur* une vue simplifiée des contraintes techniques. La ressource étant donc le *lien* entre la vue technique et la vue utilisateur. **Une autre manière de voir la ressource est de la voir comme un graphe qui a plusieurs coûts sur chaque arc**. -Par exemple, un graphe OSRM ne contient qu’un seul coût. Il permet donc de calculer des itinéraires uniquement sur un seul mode de déplacement et une seule optimisation. De la même manière, une fonction PgRouting utilise une seule colonne de coût à la fois. +Cela est utile pour OSRM par exemple. Dans ce cas, il faut donc être vigilant lors de la génération des données. Lorsque l’on fera une ressource, il est alors impératif d’utiliser une même topologie pour plusieurs calculs de coûts différents. -Afin de masquer cette contrainte technique, on va regrouper plusieurs graphes issus des mêmes données topologiques mais ayant des coûts différents. Ce regroupement sera une *ressource*. **Une ressource sera alors définie comme un ensemble de sources**. En associant plusieurs sources issues des mêmes données mais ayant un calcul de coût différent, on peut donner à l’utilisateur une vue simplifiée des contraintes techniques. La ressource est donc le *lien* entre la vue technique et la vue utilisateur. **Une autre manière de voir la ressource est de la voir comme un graphe qui a plusieurs coûts sur chaque arc**. +Mais depuis le début du projet, nous avons élargi les possibilités en permettant d'associer des sources qui n'ont pas la même topologie. Ainsi, aujourd'hui, une ressource n'est qu'un regroupement de sources. Il peut n'y en avoir qu'une. C'est souvent le cas pour PGRouting. -Il faudra donc être vigilant lors de la génération des données. Lorsque l’on fera une ressource, il sera impératif d’utiliser une même topologie pour plusieurs calculs de coûts différents. Cela peut d'ailleurs avoir un impact sur les contraintes, comme les filtres. En effet, les contraintes sont appliquées au niveau de la ressource et non d’une source. C’est un choix qui permet de simplifier la configuration. +Pour l'*administrateur*, une instance de Road2 doit pouvoir gérer plusieurs ressources. Une ressource sera notamment configurable par un fichier. Le serveur lira l’ensemble des fichiers contenus dans un dossier indiqué par la configuration générale. -De plus, une instance de Road2 doit pouvoir gérer plusieurs ressources. Une ressource sera notamment configurable par un fichier. Le serveur lira l’ensemble des fichiers contenus dans un dossier indiqué par la configuration générale. +Il est à noter que tout cela peut d'ailleurs avoir un impact sur les contraintes, comme les filtres. En effet, les contraintes sont appliquées au niveau de la ressource et non d’une source. C’est un choix qui permet de simplifier la configuration. -Enfin, Road2 est codé pour qu'il soit facile d'ajouter de nouveaux types de ressources et de sources indépendamment. Il est donc possible de créer différents types de source et de les associer au sein de divers types de ressources. +Enfin, précisons que Road2 est codé pour qu'il soit facile d'ajouter de nouveaux types de ressources et de sources indépendamment. Il est donc possible de créer différents types de source et de les associer au sein de divers types de ressources. ### 1.3 Les opérations diff --git a/documentation/developers/readme.md b/documentation/developers/readme.md index 088204c..2db5a2c 100644 --- a/documentation/developers/readme.md +++ b/documentation/developers/readme.md @@ -16,7 +16,7 @@ L'ensemble des fonctionnalités sont répertoriées à [part](./functionnalities ## Participer aux développements -Les participations à ce projet sont encouragées. L'ajout de moteurs ou d'API, bien évidemment. Mais toutes autres fonctionnalités sont les bienvenues. +Les participations à ce projet sont encouragées. L'ajout de moteurs ou d'API, bien évidemment. Mais toutes autres fonctionnalités sont les bienvenues. Encore, une fois, il vous est demandé de réaliser vos développements en partant de la branche *develop*. ### Prise en main du projet diff --git a/readme.md b/readme.md index 627a1c1..05ca3ae 100644 --- a/readme.md +++ b/readme.md @@ -67,13 +67,13 @@ Pour une discussion détaillée sur les données attendues, on pourra se référ ### Configuration -Afin que le serveur fonctionne, il est nécessaire de le [configurer](./documentation/configuration/readme.md). Il s'agit de créer une arborescence de quelques fichiers JSON, au minimum quatre, permettant l'instanciation du serveur avec des ressources. +Afin que le serveur fonctionne, il est nécessaire de le [configurer](./documentation/configuration/readme.md). Il s'agit de créer une arborescence de quelques fichiers JSON permettant l'instanciation du serveur avec des ressources. ### Lancement Une fois configuré, il est possible de lancer une instance de Road2 avec la commande: ``` -node ${road2}/src/js/road2.js --ROAD2_CONF_FILE=${configuration}/server.json +node ${road2}/src/js/road2.js --ROAD2_CONF_FILE=${configuration}/administration.json ``` ### Pour plus de détails @@ -82,11 +82,13 @@ On trouvera dans le dossier [docker/distrubutions](./docker/distributions) diff ## Participer aux développements +Les participations à ce projet sont encouragées. Il vous est demandé de réaliser vos développements en partant de la branche *develop*. + On trouvera une documentation dédiée aux développeurs [ici](./documentation/developers/readme.md). Elle indique les concepts utiles pour effectuer des développements sur Road2. Pour en savoir plus sur notre roadmap, vous pouvez regarder ce [document](./documentation/developers/roadmap.md). -De plus, il est possible d'utiliser ce [docker-compose](./docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. +Enfin, il est possible d'utiliser ce [docker-compose](./docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. ## Utilisation en production From f3d01f48308b390c27c48249c6f434a95daf5fcb Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 17 Nov 2022 18:15:51 +0100 Subject: [PATCH 17/93] fix des bbox --- .../apis/simple/1.0.0/getCapabilities_example.yaml | 6 +++--- documentation/configuration/resources/osrm.resource | 8 ++++---- documentation/configuration/sources/pgr.source | 2 +- documentation/configuration/sources/smartrouting.source | 2 +- documentation/configuration/sources/valhalla.source | 2 +- .../mocha/resources/integrationOsrmResource.js | 2 +- .../integration/mocha/resources/integrationPgrResource.js | 2 +- .../mocha/resources/integrationResourceManager.js | 8 ++++---- .../mocha/resources/integrationSmartPgrResource.js | 2 +- .../mocha/resources/integrationValhallaResource.js | 2 +- test/integration/mocha/sources/integrationOsrmSource.js | 2 +- test/integration/mocha/sources/integrationPgrSource.js | 2 +- .../integration/mocha/sources/integrationSourceManager.js | 6 +++--- .../mocha/sources/integrationValhallaSource.js | 2 +- .../resources/topology/correctOSMTopology.json | 2 +- .../resources/topology/incorrectOSMTopology.json | 2 +- test/unit/mocha/config/resources/corse.resource | 8 ++++---- test/unit/mocha/topology/testsTopology.js | 2 +- 18 files changed, 31 insertions(+), 31 deletions(-) diff --git a/documentation/apis/simple/1.0.0/getCapabilities_example.yaml b/documentation/apis/simple/1.0.0/getCapabilities_example.yaml index 7b57e9a..053392b 100644 --- a/documentation/apis/simple/1.0.0/getCapabilities_example.yaml +++ b/documentation/apis/simple/1.0.0/getCapabilities_example.yaml @@ -266,15 +266,15 @@ getCapabilities: values: {"bduni"} - id: "start" values: - bbox: "-90,-180,90,180" + bbox: "-180,-90,180,90" projection: "EPSG:4326" - id: "end" values: - bbox: "-90,-180,90,180" + bbox: "-180,-90,180,90" projection: "EPSG:4326" - id: "intermediates" values: - bbox: "-90,-180,90,180" + bbox: "-180,-90,180,90" projection: "EPSG:4326" - id: "profile" default: "car" diff --git a/documentation/configuration/resources/osrm.resource b/documentation/configuration/resources/osrm.resource index a92cd82..7620497 100644 --- a/documentation/configuration/resources/osrm.resource +++ b/documentation/configuration/resources/osrm.resource @@ -20,14 +20,14 @@ { "id": "start", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, { "id": "end", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, @@ -48,7 +48,7 @@ { "id": "intermediates", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, @@ -138,7 +138,7 @@ { "id": "coordinates", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, diff --git a/documentation/configuration/sources/pgr.source b/documentation/configuration/sources/pgr.source index 4c726e4..c1f0ed6 100644 --- a/documentation/configuration/sources/pgr.source +++ b/documentation/configuration/sources/pgr.source @@ -1,5 +1,5 @@ { - "id": "bduni-idf-car-fastest-pgr", + "id": "bduni-pgr", "description":"test pgr", "type": "pgr", "projection": "EPSG:4326", diff --git a/documentation/configuration/sources/smartrouting.source b/documentation/configuration/sources/smartrouting.source index d7c6462..3bdee18 100644 --- a/documentation/configuration/sources/smartrouting.source +++ b/documentation/configuration/sources/smartrouting.source @@ -3,7 +3,7 @@ "type": "smartrouting", "description":"test", "projection": "EPSG:4326", - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "storage": { "url": "https://wxs.ign.fr/calcul" } diff --git a/documentation/configuration/sources/valhalla.source b/documentation/configuration/sources/valhalla.source index 62b86e1..a9d959a 100644 --- a/documentation/configuration/sources/valhalla.source +++ b/documentation/configuration/sources/valhalla.source @@ -1,5 +1,5 @@ { - "id": "bdtopo-auto-valhalla", + "id": "bdtopo-valhalla", "description":"test valhalla", "type": "valhalla", "projection": "EPSG:4326", diff --git a/test/integration/mocha/resources/integrationOsrmResource.js b/test/integration/mocha/resources/integrationOsrmResource.js index afd8824..ba8123d 100644 --- a/test/integration/mocha/resources/integrationOsrmResource.js +++ b/test/integration/mocha/resources/integrationOsrmResource.js @@ -46,7 +46,7 @@ describe('Test de la classe OsrmResource', function() { ], "defaultSourceId": "corse-car-fastest", - "boundingBox": "-90,-180,90,180", + "boundingBox": "-180,-90,180,90", "defaultProjection": "EPSG:4326", "availableProjections": ["EPSG:4326","EPSG:2154"] } diff --git a/test/integration/mocha/resources/integrationPgrResource.js b/test/integration/mocha/resources/integrationPgrResource.js index 37ddd16..fdc3cde 100644 --- a/test/integration/mocha/resources/integrationPgrResource.js +++ b/test/integration/mocha/resources/integrationPgrResource.js @@ -48,7 +48,7 @@ describe('Test de la classe PgrResource', function() { ], "defaultSourceId": "test-car-fastest", - "boundingBox": "-90,-180,90,180", + "boundingBox": "-180,-90,180,90", "defaultProjection": "EPSG:4326", "availableProjections": ["EPSG:4326","EPSG:2154"] } diff --git a/test/integration/mocha/resources/integrationResourceManager.js b/test/integration/mocha/resources/integrationResourceManager.js index 711034e..6c0f930 100644 --- a/test/integration/mocha/resources/integrationResourceManager.js +++ b/test/integration/mocha/resources/integrationResourceManager.js @@ -42,7 +42,7 @@ describe('Test de la classe ResourceManager', function() { "file": "/home/docker/internal/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180" + "bbox": "-180,-90,180,90" }, "sources": [ { @@ -75,14 +75,14 @@ describe('Test de la classe ResourceManager', function() { { "id": "start", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, { "id": "end", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, @@ -103,7 +103,7 @@ describe('Test de la classe ResourceManager', function() { { "id": "intermediates", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, diff --git a/test/integration/mocha/resources/integrationSmartPgrResource.js b/test/integration/mocha/resources/integrationSmartPgrResource.js index c9c1467..ab5b8ae 100644 --- a/test/integration/mocha/resources/integrationSmartPgrResource.js +++ b/test/integration/mocha/resources/integrationSmartPgrResource.js @@ -56,7 +56,7 @@ describe('Test de la classe SmartpgrResource', function() { ], "defaultSourceId": "test-car-fastest", - "boundingBox": "-90,-180,90,180", + "boundingBox": "-180,-90,180,90", "defaultProjection": "EPSG:4326", "availableProjections": ["EPSG:4326","EPSG:2154"] } diff --git a/test/integration/mocha/resources/integrationValhallaResource.js b/test/integration/mocha/resources/integrationValhallaResource.js index 56a2267..0f2dfce 100644 --- a/test/integration/mocha/resources/integrationValhallaResource.js +++ b/test/integration/mocha/resources/integrationValhallaResource.js @@ -54,7 +54,7 @@ describe('Test de la classe ValhallaResource', function() { ], "defaultSourceId": "corse-auto-valhalla", - "boundingBox": "-90,-180,90,180", + "boundingBox": "-180,-90,180,90", "defaultProjection": "EPSG:4326", "availableProjections": ["EPSG:4326","EPSG:2154"] } diff --git a/test/integration/mocha/sources/integrationOsrmSource.js b/test/integration/mocha/sources/integrationOsrmSource.js index 7c8e886..a58073b 100644 --- a/test/integration/mocha/sources/integrationOsrmSource.js +++ b/test/integration/mocha/sources/integrationOsrmSource.js @@ -77,7 +77,7 @@ describe('Test de la classe osrmSource', function() { "file": "/home/docker/internal/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", }; diff --git a/test/integration/mocha/sources/integrationPgrSource.js b/test/integration/mocha/sources/integrationPgrSource.js index 4d71f57..b72f9e7 100644 --- a/test/integration/mocha/sources/integrationPgrSource.js +++ b/test/integration/mocha/sources/integrationPgrSource.js @@ -40,7 +40,7 @@ describe('Test de la classe pgrSource', function() { "file": "/home/docker/internal/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "base": {connected: false, connect(){this.connected = true;}}, "defaultAttributesKeyTable": {length: 0} diff --git a/test/integration/mocha/sources/integrationSourceManager.js b/test/integration/mocha/sources/integrationSourceManager.js index 83a0123..a3dde9c 100644 --- a/test/integration/mocha/sources/integrationSourceManager.js +++ b/test/integration/mocha/sources/integrationSourceManager.js @@ -63,14 +63,14 @@ describe('Test de la classe SourceManager', function() { { "id": "start", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, { "id": "end", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, @@ -91,7 +91,7 @@ describe('Test de la classe SourceManager', function() { { "id": "intermediates", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, diff --git a/test/integration/mocha/sources/integrationValhallaSource.js b/test/integration/mocha/sources/integrationValhallaSource.js index 39d0d6a..1ac57df 100644 --- a/test/integration/mocha/sources/integrationValhallaSource.js +++ b/test/integration/mocha/sources/integrationValhallaSource.js @@ -64,7 +64,7 @@ describe('Test de la classe valhallaSource', function() { "file": "/home/docker/data/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", }; diff --git a/test/integration/resources/topology/correctOSMTopology.json b/test/integration/resources/topology/correctOSMTopology.json index 2f419ca..133bdfa 100644 --- a/test/integration/resources/topology/correctOSMTopology.json +++ b/test/integration/resources/topology/correctOSMTopology.json @@ -6,5 +6,5 @@ "file": "/home/docker/internal/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180" + "bbox": "-180,-90,180,90" } \ No newline at end of file diff --git a/test/integration/resources/topology/incorrectOSMTopology.json b/test/integration/resources/topology/incorrectOSMTopology.json index af42b00..8597726 100644 --- a/test/integration/resources/topology/incorrectOSMTopology.json +++ b/test/integration/resources/topology/incorrectOSMTopology.json @@ -5,5 +5,5 @@ "file": "/home/docker/internal/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180" + "bbox": "-180,-90,180,90" } \ No newline at end of file diff --git a/test/unit/mocha/config/resources/corse.resource b/test/unit/mocha/config/resources/corse.resource index 7e85160..f6e6da5 100644 --- a/test/unit/mocha/config/resources/corse.resource +++ b/test/unit/mocha/config/resources/corse.resource @@ -11,7 +11,7 @@ "file": "/home/docker/internal/corse-latest.osm.pbf" }, "projection": "EPSG:4326", - "bbox": "-90,-180,90,180" + "bbox": "-180,-90,180,90" }, "sources": [ { @@ -44,14 +44,14 @@ { "id": "start", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, { "id": "end", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, @@ -72,7 +72,7 @@ { "id": "intermediates", "values": { - "bbox": "-90,-180,90,180", + "bbox": "-180,-90,180,90", "projection": "EPSG:4326" } }, diff --git a/test/unit/mocha/topology/testsTopology.js b/test/unit/mocha/topology/testsTopology.js index a139640..e877eca 100644 --- a/test/unit/mocha/topology/testsTopology.js +++ b/test/unit/mocha/topology/testsTopology.js @@ -14,7 +14,7 @@ describe('Test de la classe Topology', function() { "type": "osm", "description": "Corse en version OSM", "projection": "EPSG:4326", - "bbox": "-90,-180,90,180" + "bbox": "-180,-90,180,90" }; From a826e5ed64fe6185631fe73ec712b3c1a18aae00 Mon Sep 17 00:00:00 2001 From: Loic Date: Wed, 23 Nov 2022 13:12:24 +0100 Subject: [PATCH 18/93] fix for valhalla sources --- src/js/parameters/constraintParameter.js | 26 +++++++++++++++++-- src/js/parameters/parameterManager.js | 4 +-- src/js/sources/valhallaSource.js | 5 +++- .../resources/integrationValhallaResource.js | 2 +- .../sources/integrationValhallaSource.js | 4 +-- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/js/parameters/constraintParameter.js b/src/js/parameters/constraintParameter.js index f7d360c..3cb9dbb 100644 --- a/src/js/parameters/constraintParameter.js +++ b/src/js/parameters/constraintParameter.js @@ -188,6 +188,21 @@ module.exports = class ConstraintParameter extends ResourceParameter { } + } else if (this._values[i].keyType === "name-valhalla") { + + this._verification[this._values[i].key.toLowerCase()].keyType = "name-valhalla"; + + currentKeyDescription.availableOperators = new Array(); + currentKeyDescription.availableOperators.push("="); + currentKeyDescription.values = new Array(); + + for(let l = 0; l < this._values[i].availableValues.length; l++) { + + this._verification[this._values[i].key.toLowerCase()][this._values[i].availableValues[l].value] = this._values[i].availableValues[l].field; + currentKeyDescription.values.push(this._values[i].availableValues[l].value); + + } + } else { return false; } @@ -304,9 +319,10 @@ module.exports = class ConstraintParameter extends ResourceParameter { } - if (this._verification[userJson.key.toLowerCase()].keyType === "name-pgr" || this._verification[userJson.key.toLowerCase()].keyType === "name-osrm") { + let nameTable = ["name-pgr","name-osrm","name-valhalla"]; + if (nameTable.includes(this._verification[userJson.key.toLowerCase()].keyType)) { - LOGGER.debug("keyType is name-pgr ror name-osrm"); + LOGGER.debug("keyType is name-pgr ror name-osrm ror name-valhalla"); // Vérification de l'opérateur if (!userJson.operator) { @@ -468,6 +484,12 @@ module.exports = class ConstraintParameter extends ResourceParameter { } else if (this._verification[userJson.key.toLowerCase()].keyType === "name-osrm") { let field = this._verification[userJson.key.toLowerCase()][userJson.value]; + if (userJson.constraintType === 'banned') { + constraint = new Constraint(userJson.constraintType, userJson.key.toLowerCase(), field, userJson.operator, userJson.value); + } + } else if (this._verification[userJson.key.toLowerCase()].keyType === "name-valhalla") { + let field = this._verification[userJson.key.toLowerCase()][userJson.value]; + if (userJson.constraintType === 'banned') { constraint = new Constraint(userJson.constraintType, userJson.key.toLowerCase(), field, userJson.operator, userJson.value); } diff --git a/src/js/parameters/parameterManager.js b/src/js/parameters/parameterManager.js index 11bf941..c90626e 100644 --- a/src/js/parameters/parameterManager.js +++ b/src/js/parameters/parameterManager.js @@ -753,7 +753,7 @@ module.exports = class parameterManager { LOGGER.error("Le type de la cle contrainte n'est pas precise"); return false; } else { - if ( !(["name-pgr", "numerical-pgr", "name-osrm"].includes(key.keyType)) ) { + if ( !(["name-pgr", "numerical-pgr", "name-osrm","name-valhalla"].includes(key.keyType)) ) { LOGGER.error("Le type de la cle contrainte est invalide"); return false; } else { @@ -845,7 +845,7 @@ module.exports = class parameterManager { } } - } else if (key.keyType === "name-osrm") { + } else if (key.keyType === "name-osrm" || key.keyType === "name-valhalla") { if (!key.availableValues) { LOGGER.error("Les valeurs de la cle contrainte ne sont pas precisees"); diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js index fa6c83d..1413082 100644 --- a/src/js/sources/valhallaSource.js +++ b/src/js/sources/valhallaSource.js @@ -47,8 +47,11 @@ module.exports = class valhallaSource extends Source { // Initialisation des coûts for (let i = 0; i < sourceJsonObject.costs.length; i++) { - Object.defineProperty(this._costs, sourceJsonObject.costs[i].profile, { value: new Object(), configurable: true, enumerable: true, writable: true }); + if (!this._costs[sourceJsonObject.costs[i].profile]) { + Object.defineProperty(this._costs, sourceJsonObject.costs[i].profile, { value: new Object(), configurable: true, enumerable: true, writable: true }); + } Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile], sourceJsonObject.costs[i].optimization, { value: new Object(), configurable: true, enumerable: true, writable: true }); + Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile], sourceJsonObject.costs[i].costType, { value: new Object(), configurable: true, enumerable: true, writable: true }); Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].optimization], "costing", { value: sourceJsonObject.costs[i].costing, configurable: true, enumerable: true, writable: true }); Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].costType], "costing", { value: sourceJsonObject.costs[i].costing, configurable: true, enumerable: true, writable: true }); } diff --git a/test/integration/mocha/resources/integrationValhallaResource.js b/test/integration/mocha/resources/integrationValhallaResource.js index 0f2dfce..b248a59 100644 --- a/test/integration/mocha/resources/integrationValhallaResource.js +++ b/test/integration/mocha/resources/integrationValhallaResource.js @@ -43,7 +43,7 @@ describe('Test de la classe ValhallaResource', function() { "configuration": { "costing": "auto", "storage": { - "file": "/home/docker/config/costs_calculation.json" + "file": "/home/docker/config/costs_calculation_sample.json" } } } diff --git a/test/integration/mocha/sources/integrationValhallaSource.js b/test/integration/mocha/sources/integrationValhallaSource.js index 1ac57df..fffb9d4 100644 --- a/test/integration/mocha/sources/integrationValhallaSource.js +++ b/test/integration/mocha/sources/integrationValhallaSource.js @@ -20,7 +20,7 @@ describe('Test de la classe valhallaSource', function() { }, "/home/docker/config": { "graph_bdtopo.lua": "", - "costs_calculation.json": "", + "costs_calculation_sample.json": "", }, "/home/docker/data/corse-latest-valhalla-tiles": { } @@ -49,7 +49,7 @@ describe('Test de la classe valhallaSource', function() { "configuration": { "costing": "auto", "storage": { - "file": "/home/docker/config/costs_calculation.json" + "file": "/home/docker/config/costs_calculation_sample.json" } } } From 0b9e28d519dc40588bc3ece1b10cd4f7b716ad26 Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 25 Nov 2022 15:50:11 +0100 Subject: [PATCH 19/93] =?UTF-8?q?[tests]=20maj=20d'un=20test=20sur=20les?= =?UTF-8?q?=20donn=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/dev/readme.md | 1 + test/functional/request/cucumber/data/bduni/common/normale.json | 2 +- .../cucumber/features/req-simple-1.0.0-smartrouting.feature | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/dev/readme.md b/docker/dev/readme.md index 1d608bd..e6508f5 100644 --- a/docker/dev/readme.md +++ b/docker/dev/readme.md @@ -27,6 +27,7 @@ Si on utilise ces Dockerfile avec un VPN, on vérifiera que les configurations D Si on utilise ces Dockerfile sur un réseau avec lequel il peut y avoir des problèmes d'IP, il sera utile de dédier à Docker une plage d'IP non utilisées: - L'attribut `bip` du fichier ``/etc/docker/daemon.json` permet de préciser une plage d'IP. - Si bip a été rempli, on veillera à ce que ces IP soient bien ajouter à l'interface `docker0`. La commande `sudo ip route add {plage_ip} dev docker0` permet de le faire. +- On pourra aussi avoir besoin d'ajouter le réseau créer par ce compose : `sudo ip route add {plage_ip} dev br-{id_du_network} proto kernel scope link src {ip_gateway}` où l'id est obtenu en faisant un `docker network ls`. Cette plage d'IP sera différente de celle attribuée à la stack Road2 lancée via docker-compose (eg. celui présent dans le fichier `.env`). diff --git a/test/functional/request/cucumber/data/bduni/common/normale.json b/test/functional/request/cucumber/data/bduni/common/normale.json index 12498d4..1be3405 100644 --- a/test/functional/request/cucumber/data/bduni/common/normale.json +++ b/test/functional/request/cucumber/data/bduni/common/normale.json @@ -1 +1 @@ -{"resource":"bduni-idf-osrm","resourceVersion":"01/07/2021","start":"2.342023,48.84574","end":"2.321707,48.845556","profile":"car","optimization":"fastest","geometry":{"coordinates":[[2.342023,48.84574],[2.341431,48.845918],[2.341353,48.845934],[2.341214,48.845967],[2.341154,48.84609],[2.340819,48.846724],[2.34079,48.846772],[2.340715,48.846878],[2.340634,48.846972],[2.340797,48.84727],[2.34084,48.847339],[2.340833,48.847402],[2.34083,48.847427],[2.340819,48.847451],[2.340804,48.847469],[2.340781,48.847489],[2.340753,48.847507],[2.340715,48.847523],[2.340611,48.847552],[2.340093,48.847664],[2.339948,48.84782],[2.339443,48.84842],[2.339191,48.848721],[2.339119,48.848803],[2.339051,48.848935],[2.339043,48.849092],[2.338961,48.849125],[2.338839,48.849169],[2.338772,48.849183],[2.338682,48.84919],[2.338575,48.849192],[2.33843,48.849181],[2.338139,48.849225],[2.337843,48.849261],[2.337594,48.849285],[2.337328,48.849298],[2.336388,48.849282],[2.335956,48.849274],[2.335676,48.849257],[2.335449,48.849239],[2.335242,48.849202],[2.3351,48.84917],[2.335014,48.849148],[2.334846,48.849116],[2.334587,48.849063],[2.334309,48.849013],[2.334105,48.848977],[2.333713,48.848897],[2.333197,48.848797],[2.332955,48.848754],[2.332766,48.848688],[2.332552,48.848625],[2.332473,48.848624],[2.332386,48.848613],[2.331662,48.84842],[2.331044,48.848247],[2.329567,48.847825],[2.328125,48.847397],[2.327933,48.847347],[2.327818,48.847315],[2.327442,48.847209],[2.32739,48.847207],[2.327336,48.84721],[2.327284,48.847225],[2.32717,48.847265],[2.326987,48.847076],[2.326868,48.84695],[2.326783,48.84687],[2.325614,48.845661],[2.325193,48.845231],[2.324544,48.844565],[2.324174,48.844181],[2.324099,48.844163],[2.324033,48.844145],[2.323799,48.8441],[2.323365,48.844036],[2.323193,48.844091],[2.322905,48.844184],[2.321285,48.844725],[2.320553,48.844967],[2.320121,48.845097],[2.320575,48.845222],[2.321707,48.845556]],"type":"LineString"},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.320121,48.844036,2.342023,48.849298],"distance":2256.9,"duration":3.0349999999999997,"constraints":[],"portions":[{"start":"2.342023,48.84574","end":"2.321707,48.845556","distance":2256.9,"duration":3.0349999999999997,"bbox":[2.320121,48.844036,2.342023,48.849298],"steps":[{"geometry":{"coordinates":[[2.342023,48.84574],[2.341431,48.845918],[2.341353,48.845934],[2.341214,48.845967]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R ROYER-COLLARD","nom_1_droite":"R ROYER-COLLARD","cpx_numero":"","cpx_toponyme":""}},"distance":64.4,"duration":0.13},{"geometry":{"coordinates":[[2.341214,48.845967],[2.341154,48.84609],[2.340819,48.846724],[2.34079,48.846772],[2.340715,48.846878],[2.340634,48.846972]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R GAY-LUSSAC","nom_1_droite":"R GAY-LUSSAC","cpx_numero":"","cpx_toponyme":""}},"distance":119.8,"duration":0.18000000000000002},{"geometry":{"coordinates":[[2.340634,48.846972],[2.340797,48.84727],[2.34084,48.847339]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL EDMOND ROSTAND","nom_1_droite":"PL EDMOND ROSTAND","cpx_numero":"","cpx_toponyme":""}},"distance":43.5,"duration":0.06666666666666667},{"geometry":{"coordinates":[[2.34084,48.847339],[2.340833,48.847402],[2.34083,48.847427],[2.340819,48.847451],[2.340804,48.847469],[2.340781,48.847489],[2.340753,48.847507],[2.340715,48.847523],[2.340611,48.847552],[2.340093,48.847664]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL EDMOND ROSTAND","nom_1_droite":"PL EDMOND ROSTAND","cpx_numero":"","cpx_toponyme":""}},"distance":72,"duration":0.13666666666666666},{"geometry":{"coordinates":[[2.340093,48.847664],[2.339948,48.84782],[2.339443,48.84842],[2.339191,48.848721],[2.339119,48.848803],[2.339051,48.848935],[2.339043,48.849092]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE MEDICIS","nom_1_droite":"R DE MEDICIS","cpx_numero":"","cpx_toponyme":""}},"distance":178.4,"duration":0.20166666666666666},{"geometry":{"coordinates":[[2.339043,48.849092],[2.338961,48.849125],[2.338839,48.849169],[2.338772,48.849183],[2.338682,48.84919],[2.338575,48.849192],[2.33843,48.849181]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL PAUL CLAUDEL","nom_1_droite":"PL PAUL CLAUDEL","cpx_numero":"","cpx_toponyme":""}},"distance":47.5,"duration":0.07166666666666667},{"geometry":{"coordinates":[[2.33843,48.849181],[2.338139,48.849225],[2.337843,48.849261],[2.337594,48.849285],[2.337328,48.849298],[2.336388,48.849282],[2.335956,48.849274],[2.335676,48.849257],[2.335449,48.849239],[2.335242,48.849202],[2.3351,48.84917],[2.335014,48.849148],[2.334846,48.849116],[2.334587,48.849063],[2.334309,48.849013],[2.334105,48.848977],[2.333713,48.848897],[2.333197,48.848797],[2.332955,48.848754],[2.332766,48.848688],[2.332552,48.848625],[2.332473,48.848624],[2.332386,48.848613],[2.331662,48.84842],[2.331044,48.848247],[2.329567,48.847825],[2.328125,48.847397],[2.327933,48.847347],[2.327818,48.847315],[2.327442,48.847209],[2.32739,48.847207],[2.327336,48.84721],[2.327284,48.847225],[2.32717,48.847265]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE VAUGIRARD","nom_1_droite":"R DE VAUGIRARD","cpx_numero":"","cpx_toponyme":""}},"distance":870.6,"duration":1.1016666666666666},{"geometry":{"coordinates":[[2.32717,48.847265],[2.326987,48.847076],[2.326868,48.84695],[2.326783,48.84687],[2.325614,48.845661],[2.325193,48.845231],[2.324544,48.844565],[2.324174,48.844181]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE RENNES","nom_1_droite":"R DE RENNES","cpx_numero":"","cpx_toponyme":""}},"distance":407.1,"duration":0.5433333333333333},{"geometry":{"coordinates":[[2.324174,48.844181],[2.324099,48.844163],[2.324033,48.844145],[2.323799,48.8441],[2.323365,48.844036]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL DU 18 JUIN 1940","nom_1_droite":"PL DU 18 JUIN 1940","cpx_numero":"","cpx_toponyme":""}},"distance":61.5,"duration":0.08166666666666668},{"geometry":{"coordinates":[[2.323365,48.844036],[2.323193,48.844091],[2.322905,48.844184],[2.321285,48.844725],[2.320553,48.844967],[2.320121,48.845097]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"BD DU MONTPARNASSE","nom_1_droite":"BD DU MONTPARNASSE","cpx_numero":"","cpx_toponyme":""}},"distance":265.2,"duration":0.35333333333333333},{"geometry":{"coordinates":[[2.320121,48.845097],[2.320575,48.845222],[2.321707,48.845556]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE VAUGIRARD","nom_1_droite":"R DE VAUGIRARD","cpx_numero":"","cpx_toponyme":""}},"distance":126.8,"duration":0.16833333333333333},{"geometry":{"coordinates":[[2.321707,48.845556],[2.321707,48.845556]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE VAUGIRARD","nom_1_droite":"R DE VAUGIRARD","cpx_numero":"","cpx_toponyme":""}},"distance":0,"duration":0}]}]} \ No newline at end of file +{"resource":"bduni-idf-osrm","resourceVersion":"2022-11-24","start":"2.342023,48.84574","end":"2.321707,48.845556","profile":"car","optimization":"fastest","geometry":{"coordinates":[[2.342023,48.84574],[2.342245,48.845673],[2.342677,48.845592],[2.342952,48.846098],[2.343346,48.846797],[2.342813,48.84691],[2.342433,48.846991],[2.341951,48.847098],[2.34169,48.847155],[2.3412,48.847258],[2.34095,48.847314],[2.34084,48.847339],[2.340833,48.847402],[2.34083,48.847427],[2.340819,48.847451],[2.340804,48.847469],[2.340781,48.847489],[2.340753,48.847507],[2.340715,48.847523],[2.340611,48.847552],[2.340093,48.847664],[2.339948,48.84782],[2.339443,48.84842],[2.339191,48.848721],[2.339119,48.848803],[2.339051,48.848935],[2.339043,48.849092],[2.338961,48.849125],[2.338839,48.849169],[2.338772,48.849183],[2.338682,48.84919],[2.338575,48.849192],[2.33843,48.849181],[2.338139,48.849225],[2.337843,48.849261],[2.337594,48.849285],[2.337328,48.849298],[2.336388,48.849282],[2.335956,48.849274],[2.335676,48.849257],[2.335449,48.849239],[2.335242,48.849202],[2.3351,48.84917],[2.335014,48.849148],[2.334846,48.849116],[2.334587,48.849063],[2.334309,48.849013],[2.334105,48.848977],[2.333713,48.848897],[2.333197,48.848797],[2.332955,48.848754],[2.332766,48.848688],[2.332552,48.848625],[2.332473,48.848624],[2.332386,48.848613],[2.331662,48.84842],[2.331044,48.848247],[2.329567,48.847825],[2.328125,48.847397],[2.327933,48.847347],[2.327818,48.847315],[2.327442,48.847209],[2.32739,48.847207],[2.327336,48.84721],[2.327284,48.847225],[2.32717,48.847265],[2.326987,48.847076],[2.326868,48.84695],[2.326783,48.84687],[2.325614,48.845661],[2.325193,48.845231],[2.324544,48.844565],[2.324174,48.844181],[2.324099,48.844163],[2.324033,48.844145],[2.323799,48.8441],[2.323365,48.844036],[2.323193,48.844091],[2.322905,48.844184],[2.321285,48.844725],[2.320553,48.844967],[2.320121,48.845097],[2.320575,48.845222],[2.321707,48.845556]],"type":"LineString"},"crs":"EPSG:4326","distanceUnit":"meter","timeUnit":"minute","bbox":[2.320121,48.844036,2.343346,48.849298],"distance":2415.7,"duration":3.2216666666666667,"constraints":[],"portions":[{"start":"2.342023,48.84574","end":"2.321707,48.845556","distance":2415.7,"duration":3.2216666666666667,"bbox":[2.320121,48.844036,2.343346,48.849298],"steps":[{"geometry":{"coordinates":[[2.342023,48.84574],[2.342245,48.845673],[2.342677,48.845592]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R ROYER-COLLARD","nom_1_droite":"R ROYER-COLLARD","cpx_numero":"","cpx_toponyme":""}},"distance":50.8,"duration":0.075,"instruction":{"type":"depart"}},{"geometry":{"coordinates":[[2.342677,48.845592],[2.342952,48.846098],[2.343346,48.846797]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R SAINT-JACQUES","nom_1_droite":"R SAINT-JACQUES","cpx_numero":"","cpx_toponyme":""}},"distance":142.7,"duration":0.215,"instruction":{"type":"turn","modifier":"left"}},{"geometry":{"coordinates":[[2.343346,48.846797],[2.342813,48.84691],[2.342433,48.846991],[2.341951,48.847098],[2.34169,48.847155],[2.3412,48.847258],[2.34095,48.847314],[2.34084,48.847339]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R SOUFFLOT","nom_1_droite":"R SOUFFLOT","cpx_numero":"","cpx_toponyme":""}},"distance":193.1,"duration":0.25833333333333336,"instruction":{"type":"turn","modifier":"left"}},{"geometry":{"coordinates":[[2.34084,48.847339],[2.340833,48.847402],[2.34083,48.847427],[2.340819,48.847451],[2.340804,48.847469],[2.340781,48.847489],[2.340753,48.847507],[2.340715,48.847523],[2.340611,48.847552],[2.340093,48.847664]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL EDMOND ROSTAND","nom_1_droite":"PL EDMOND ROSTAND","cpx_numero":"","cpx_toponyme":""}},"distance":72,"duration":0.14,"instruction":{"type":"turn","modifier":"slight right"}},{"geometry":{"coordinates":[[2.340093,48.847664],[2.339948,48.84782],[2.339443,48.84842],[2.339191,48.848721],[2.339119,48.848803],[2.339051,48.848935],[2.339043,48.849092]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE MEDICIS","nom_1_droite":"R DE MEDICIS","cpx_numero":"","cpx_toponyme":""}},"distance":178.4,"duration":0.2033333333333333,"instruction":{"type":"new name","modifier":"right"}},{"geometry":{"coordinates":[[2.339043,48.849092],[2.338961,48.849125],[2.338839,48.849169],[2.338772,48.849183],[2.338682,48.84919],[2.338575,48.849192],[2.33843,48.849181]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL PAUL CLAUDEL","nom_1_droite":"PL PAUL CLAUDEL","cpx_numero":"","cpx_toponyme":""}},"distance":47.5,"duration":0.07,"instruction":{"type":"turn","modifier":"left"}},{"geometry":{"coordinates":[[2.33843,48.849181],[2.338139,48.849225],[2.337843,48.849261],[2.337594,48.849285],[2.337328,48.849298],[2.336388,48.849282],[2.335956,48.849274],[2.335676,48.849257],[2.335449,48.849239],[2.335242,48.849202],[2.3351,48.84917],[2.335014,48.849148],[2.334846,48.849116],[2.334587,48.849063],[2.334309,48.849013],[2.334105,48.848977],[2.333713,48.848897],[2.333197,48.848797],[2.332955,48.848754],[2.332766,48.848688],[2.332552,48.848625],[2.332473,48.848624],[2.332386,48.848613],[2.331662,48.84842],[2.331044,48.848247],[2.329567,48.847825],[2.328125,48.847397],[2.327933,48.847347],[2.327818,48.847315],[2.327442,48.847209],[2.32739,48.847207],[2.327336,48.84721],[2.327284,48.847225],[2.32717,48.847265]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE VAUGIRARD","nom_1_droite":"R DE VAUGIRARD","cpx_numero":"","cpx_toponyme":""}},"distance":870.6,"duration":1.1116666666666666,"instruction":{"type":"new name","modifier":"straight"}},{"geometry":{"coordinates":[[2.32717,48.847265],[2.326987,48.847076],[2.326868,48.84695],[2.326783,48.84687],[2.325614,48.845661],[2.325193,48.845231],[2.324544,48.844565],[2.324174,48.844181]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE RENNES","nom_1_droite":"R DE RENNES","cpx_numero":"","cpx_toponyme":""}},"distance":407.1,"duration":0.5433333333333333,"instruction":{"type":"turn","modifier":"left"}},{"geometry":{"coordinates":[[2.324174,48.844181],[2.324099,48.844163],[2.324033,48.844145],[2.323799,48.8441],[2.323365,48.844036]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"PL DU 18 JUIN 1940","nom_1_droite":"PL DU 18 JUIN 1940","cpx_numero":"","cpx_toponyme":""}},"distance":61.5,"duration":0.08,"instruction":{"type":"turn","modifier":"right"}},{"geometry":{"coordinates":[[2.323365,48.844036],[2.323193,48.844091],[2.322905,48.844184],[2.321285,48.844725],[2.320553,48.844967],[2.320121,48.845097]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"BD DU MONTPARNASSE","nom_1_droite":"BD DU MONTPARNASSE","cpx_numero":"","cpx_toponyme":""}},"distance":265.2,"duration":0.3549999999999999,"instruction":{"type":"turn","modifier":"slight right"}},{"geometry":{"coordinates":[[2.320121,48.845097],[2.320575,48.845222],[2.321707,48.845556]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE VAUGIRARD","nom_1_droite":"R DE VAUGIRARD","cpx_numero":"","cpx_toponyme":""}},"distance":126.8,"duration":0.16999999999999998,"instruction":{"type":"turn","modifier":"sharp right"}},{"geometry":{"coordinates":[[2.321707,48.845556],[2.321707,48.845556]],"type":"LineString"},"attributes":{"name":{"nom_1_gauche":"R DE VAUGIRARD","nom_1_droite":"R DE VAUGIRARD","cpx_numero":"","cpx_toponyme":""}},"distance":0,"duration":0,"instruction":{"type":"arrive"}}]}]} \ No newline at end of file diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature index fb704ae..7a08241 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature @@ -52,7 +52,7 @@ Feature: Road2-SMARTROUTING And the response should contain a complete and valid iso And the response should contain an attribute "constraints.[0].key" - Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource smartpgr avec appel à la source pgr + Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource smartpgr avec appel à la source smartrouting Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone-smartpgr" And with query parameters: From 225b3af5184585d046c40af29147e05a9fec65f3 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 28 Nov 2022 14:00:10 +0100 Subject: [PATCH 20/93] maj des submodules --- docker/motors/pgrouting-procedures | 2 +- docker/route-graph-generator | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/motors/pgrouting-procedures b/docker/motors/pgrouting-procedures index 6178fb8..4336f8d 160000 --- a/docker/motors/pgrouting-procedures +++ b/docker/motors/pgrouting-procedures @@ -1 +1 @@ -Subproject commit 6178fb87383b55c4d0ba22a2305bf12168038e52 +Subproject commit 4336f8da74d76ed95971113f4d878525ca3ca2d5 diff --git a/docker/route-graph-generator b/docker/route-graph-generator index 7fcbda6..602f543 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit 7fcbda665e52a4ff61319ff1a1eb5b284b0127b7 +Subproject commit 602f5432ac37128b1a3069e195ad36b045d471e7 From ac08d5c17b2f5ec2e69c44b511c811ab9758a70a Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 29 Nov 2022 13:27:33 +0100 Subject: [PATCH 21/93] [test] maj des tests de charge --- docker/dev/docker-compose.yml | 1 + docker/test/Dockerfile | 7 +- docker/test/compose.env.example | 7 +- docker/test/docker-compose.yml | 22 +- docker/test/readme.md | 43 ++- .../{road2_parameters_iso.ssv => isoPgr.ssv} | 0 .../user-files/resources/road2_parameters.ssv | 101 ------ .../road2_parameters_idf_pgr_wo_int.ssv | 323 ------------------ .../user-files/resources/routeOsrm.ssv | 101 ++++++ .../gatling/user-files/resources/routePgr.ssv | 101 ++++++ .../{road2_route.scala => dataOsm.scala} | 6 +- .../{road2_iso.scala => isoPgr.scala} | 6 +- ...{road2_route_pgr.scala => routeOsrm.scala} | 8 +- .../user-files/simulations/routePgr.scala | 23 ++ test/load/readme.md | 7 +- 15 files changed, 291 insertions(+), 465 deletions(-) rename test/load/gatling/user-files/resources/{road2_parameters_iso.ssv => isoPgr.ssv} (100%) delete mode 100644 test/load/gatling/user-files/resources/road2_parameters.ssv delete mode 100644 test/load/gatling/user-files/resources/road2_parameters_idf_pgr_wo_int.ssv create mode 100644 test/load/gatling/user-files/resources/routeOsrm.ssv create mode 100644 test/load/gatling/user-files/resources/routePgr.ssv rename test/load/gatling/user-files/simulations/{road2_route.scala => dataOsm.scala} (74%) rename test/load/gatling/user-files/simulations/{road2_iso.scala => isoPgr.scala} (73%) rename test/load/gatling/user-files/simulations/{road2_route_pgr.scala => routeOsrm.scala} (65%) create mode 100644 test/load/gatling/user-files/simulations/routePgr.scala diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index b0edc31..7def3fc 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -73,6 +73,7 @@ volumes: networks: iti-network: + # Nom utilisé ailleurs dans ce projet name: iti-network driver: bridge ipam: diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile index 52b48a6..f89c2ec 100644 --- a/docker/test/Dockerfile +++ b/docker/test/Dockerfile @@ -1,11 +1,8 @@ -FROM debian:buster-slim +FROM r-base:latest LABEL maintainer="IGN " LABEL version="1.0" -### MAJ et installation de R -RUN apt -y update && apt install -y r-base - ### Récupération des sources du script de génération des requêtes aléatoires WORKDIR /home/docker COPY test/load/random-route-generator ./ @@ -13,4 +10,4 @@ COPY test/load/random-iso-generator ./ VOLUME ["/home/docker/data"] -CMD ["R", "-f", "/home/docker/routeGenerator.R", "--args", "/home/docker/data/road2_parameters.ssv", "100", "corse-osm", "8.61", "41.40", "9.52", "42.62"] +CMD ["R", "-f", "/home/docker/routeGenerator.R", "--args", "/home/docker/data/road2_parameters.ssv", "100", "data-osm", "8.61", "41.40", "9.52", "42.62"] diff --git a/docker/test/compose.env.example b/docker/test/compose.env.example index a952ecd..2be6f08 100644 --- a/docker/test/compose.env.example +++ b/docker/test/compose.env.example @@ -1,8 +1,8 @@ # Exemple de fichier de variables d'environnements -# Variables pour la génération de fichier aléatoire +# Variables pour la génération de données aléatoires nb_line=100 -resource=corse-osm +resource=data-osm xmin=8.61 ymin=41.40 xmax=9.52 @@ -10,4 +10,5 @@ ymax=42.62 # Variables pour gatling gatling_results=/home/user/gatling/results -gatling_user-files=/home/user/gatling/user-files \ No newline at end of file +gatling_user_files=/home/user/gatling/user-files +gatling_scenario=routeOsrm \ No newline at end of file diff --git a/docker/test/docker-compose.yml b/docker/test/docker-compose.yml index 1edb29a..ab9d2b4 100644 --- a/docker/test/docker-compose.yml +++ b/docker/test/docker-compose.yml @@ -1,16 +1,12 @@ version: "3.7" services: - load-road2-generation: + generate-load-data: build: context: ../.. dockerfile: ./docker/test/Dockerfile - args: - - dnsIP=${dns_ip} - - dnsHost=${dns_host} - - proxy=${proxy} - image: load-road2-generation - container_name: load-road2-generation-launch + image: generate-load-data + container_name: generate-load-data-launch command: ["R", "-f", "/home/docker/routeGenerator.R", "--args", "/home/docker/data/road2_parameters.ssv", "${nb_line}", "${resource}", "${xmin}", "${ymin}", "${xmax}", "${ymax}"] volumes: - iti-load-data-volume:/home/docker/data @@ -21,9 +17,15 @@ services: volumes: - iti-load-data-volume:/home/docker/data - ${gatling_results}:/opt/gatling/results - - ${gatling_user-files}:/opt/gatling/user-files - command: "-s road2LoadTest" + - ${gatling_user_files}:/opt/gatling/user-files + command: ["-s", "${gatling_scenario}"] volumes: iti-load-data-volume: - name: iti-load-data-volume \ No newline at end of file + name: iti-load-data-volume + +networks: + default: + # Nom du réseau utilisé dans le docker-compose du dossier ../dev/ + name: iti-network + external: true \ No newline at end of file diff --git a/docker/test/readme.md b/docker/test/readme.md index a9b0644..6b476f8 100644 --- a/docker/test/readme.md +++ b/docker/test/readme.md @@ -1,4 +1,4 @@ -# Docker pour faire des tests de charge +# Docker-compose pour faire des tests # Construction et utilisation avec docker-compose @@ -6,16 +6,47 @@ Pour utiliser `docker-compose`, il suffit de : - installer `docker`. -- se placer dans le dossier `/docker/load/` du projet Road2. +- se placer dans le dossier `/docker/test/` du projet Road2. - créer un fichier `.env` à côté du `docker-compose.yml` qui sera une copie adaptée du `compose.env.example` ## Construction des images -Il possible d'utiliser les Dockerfiles de chaque projet pour builder les images unes par une. Mais cela peut se faire automatiquement via docker-compose. +Il possible d'utiliser les Dockerfiles de chaque projet pour builder les images une par une. Mais cela peut se faire automatiquement via docker-compose. Il suffit de lancer la commande `docker-compose build`. -## Démarrage des services +## Lancer des tests -Pour lancer un service, il suffit d'exécuter la commande `docker-compose up $service` avec : -- `$service=load-road2` pour lancer les tests. +### Tests de charge avec gatling + +Après avoir rempli le `.env` en pointant, par exemple, sur le `user-files` de ce [dépôt](../../test/load/gatling/user-files) et en ayant pris soin de choisir un scénario, il suffit d'exécuter la commande : +``` +# Une fois le .env modifié pour choisir le scénario notamment +# Choisir le scénario (attention road2Docker ne fonctionne pas tant que docker-compose up generate-load-data n'a pas été appelé au moins une fois) +docker-compose up load-road2 +``` + +## Générer des données pour les tests + +### Tests de charge avec gatling + +Par défaut, l'image docker de r2gg permet de générer des données pour Road2 qui sont issues de données OSM. Dans ce cas, ce dépôt contient déjà des requêtes et des scénarii gatling permettant de tester Road2 sur ces données. + +Mais si l'image docker de r2gg a été utilisée pour créer une ressource pointant sur un endroit différent, il sera nécessaire de générer des données pour les tests. + +Pour cela, il suffira de modifier la bbox du `.env` et de lancer la commande suivante : +``` +# Une fois le .env modifié +docker-compose up generate-load-data +``` + +Cette commande lance la génération d'un fichier `ssv` dans un volume docker. Ce fichier est ensuite proposé dans les scénarii gatling sous le nom de `dataOsm`. Par ailleurs, ce scénario ne fonctionnera donc pas tant qu'une donnée n'aura pas été générée avec ce docker-compose. +``` +# Après la génération via ce docker-compose +# Modifier le .env pour choisir le scénario 'dataOsm' +docker-compose up load-road2 +``` + +## Utiliser des données et scénarii de la machine hôte + +Si on souhaite utiliser des données et des scénarii stockés sur la machine hôte, il suffira de modifier le `.env` pour pointer vers un autre `user-files`. diff --git a/test/load/gatling/user-files/resources/road2_parameters_iso.ssv b/test/load/gatling/user-files/resources/isoPgr.ssv similarity index 100% rename from test/load/gatling/user-files/resources/road2_parameters_iso.ssv rename to test/load/gatling/user-files/resources/isoPgr.ssv diff --git a/test/load/gatling/user-files/resources/road2_parameters.ssv b/test/load/gatling/user-files/resources/road2_parameters.ssv deleted file mode 100644 index 30a590c..0000000 --- a/test/load/gatling/user-files/resources/road2_parameters.ssv +++ /dev/null @@ -1,101 +0,0 @@ -resource;start;end;intermediates;profile;optimization;geometryFormat;getSteps;getBbox -corse-osm;41.8390713617206,9.43703397945734;42.334460593434,8.81305585236754;;;;polyline;true;true -corse-osm;42.334460593434,8.81305585236754;42.0576301801484,8.92986093250336;;car;fastest;polyline;true;true -corse-osm;42.5176797365304,9.29212701178389;41.6038459739462,9.08565157287987;42.3690578503627,9.04352678218158|42.5137092300737,9.30879551293794;;;polyline;true;true -corse-osm;42.0576301801484,8.92986093250336;41.7700933746574,9.26606619605562;42.4888796936115,9.40771683264757;car;fastest;polyline;true;true -corse-osm;41.8898966115573,9.14893843763275;42.0045748996176,9.4801379238395;;;fastest;polyline;true;true -corse-osm;41.6038459739462,9.08565157287987;41.5443718961766,8.81483415379655;;car;fastest;polyline;true;true -corse-osm;41.5208031673077,9.04400089748669;42.4807446733722,8.82998053117189;;car;;polyline;true;true -corse-osm;41.7700933746574,9.26606619605562;41.7168139981618,8.96842432008358;;car;;polyline;true;true -corse-osm;41.5691899219435,8.61595866435207;42.1262043018127,9.49186793667497;;;;polyline;true;true -corse-osm;42.0045748996176,9.4801379238395;42.5137092300737,9.30879551293794;;car;fastest;polyline;true;true -corse-osm;42.5515449205646,8.85099329014542;41.6805704825697,9.08087568214163;;;;polyline;true;true -corse-osm;41.5443718961766,8.81483415379655;42.1700654321304,9.36840610945132;;;fastest;polyline;true;true -corse-osm;42.0174223158089,9.17767921910621;42.3719795257691,9.21603240483208;;;fastest;polyline;true;true -corse-osm;42.4807446733722,8.82998053117189;41.819807130415,8.77448105972027;;;;polyline;true;true -corse-osm;42.1330393094104,9.00609113303013;42.378296860843,9.1144110000669;;;fastest;polyline;true;true -corse-osm;41.7168139981618,8.96842432008358;42.4030268945405,8.87973424090771;;;fastest;polyline;true;true -corse-osm;42.5866242765915,8.74477297541685;42.5958667070745,9.39867358952761;;;fastest;polyline;true;true -corse-osm;42.1262043018127,9.49186793667497;42.5686403946159,8.91411050735973;41.9089993005665,9.03362404228421;car;fastest;polyline;true;true -corse-osm;42.0322090332024,9.50982478241669;41.5795820110664,9.312642352283;41.6396245398792,8.77595625129528|41.7625999935204,8.81336904539494;;;polyline;true;true -corse-osm;42.5137092300737,9.30879551293794;41.4012516786857,9.04828990633832;;;fastest;polyline;true;true -corse-osm;42.0873594105476,9.25942658633692;41.5776313054049,8.92804898730246;41.8145273264637,9.3352991024428;;fastest;polyline;true;true -corse-osm;41.6805704825697,9.08087568214163;42.1666513166623,9.00361947242636;;car;fastest;polyline;true;true -corse-osm;42.0815073242411,9.27633386080852;41.7078604440065,9.36320603026543;41.9084671810875,9.25054778496502;car;fastest;polyline;true;true -corse-osm;42.1700654321304,9.36840610945132;41.6666503222939,8.74630874421447;;;fastest;polyline;true;true -corse-osm;41.6851247442979,9.43301792607177;42.506083633448,8.82026979235467;41.452086844258,9.27340428662486|42.5137092300737,9.30879551293794;;;polyline;true;true -corse-osm;42.3719795257691,9.21603240483208;41.9555506897019,8.68532789486228;;;fastest;polyline;true;true -corse-osm;41.7175621405244,8.75750602712855;41.8445159427682,8.89615235476755;42.2842764702672,8.82059491696069;;;polyline;true;true -corse-osm;41.819807130415,8.77448105972027;41.6849748234777,9.32622428155271;;car;fastest;polyline;true;true -corse-osm;41.6866839826386,8.93716305163223;41.858134907037,9.20794566819444;;;fastest;polyline;true;true -corse-osm;42.378296860843,9.1144110000669;41.4660151358461,8.8860222974699;42.0873594105476,9.25942658633692;;fastest;polyline;true;true -corse-osm;41.6749367836071,8.96070477749221;41.403155911793,9.17498027076479;;;fastest;polyline;true;true -corse-osm;42.4030268945405,8.87973424090771;41.6874637901364,9.35961340215057;42.2551984808268,9.03867767836666|41.5086117511056,8.82709097801242;;fastest;polyline;true;true -corse-osm;41.6723431202676,8.8915827107965;42.3936194989178,9.29247102554887;;car;;polyline;true;true -corse-osm;42.5958667070745,9.39867358952761;42.2634152425406,8.89967834537383;42.5958667070745,9.39867358952761|42.4757081105839,9.00439814660465;;;polyline;true;true -corse-osm;41.6429873885401,9.18426055066753;41.4106993408641,9.23118901771959;;car;fastest;polyline;true;true -corse-osm;42.5686403946159,8.91411050735973;42.2733301856043,8.74593808557372;;;fastest;polyline;true;true -corse-osm;41.6214101031329,8.63401646217331;42.5111556108762,9.19636671333108;;;;polyline;true;true -corse-osm;41.5795820110664,9.312642352283;41.9834078009333,9.19273838646011;;;;polyline;true;true -corse-osm;42.2730661670724,8.84360980622703;41.9441022483632,8.8211155277933;41.403155911793,9.17498027076479;;;polyline;true;true -corse-osm;41.4012516786857,9.04828990633832;41.8017727926048,9.4208018352068;;;fastest;polyline;true;true -corse-osm;41.7625999935204,8.81336904539494;41.5086117511056,8.82709097801242;;;fastest;polyline;true;true -corse-osm;41.5776313054049,8.92804898730246;41.8001302840142,8.72909356556134;;;fastest;polyline;true;true -corse-osm;42.0097980653122,9.3990617027157;41.8772974060429,9.33969355768524;;;fastest;polyline;true;true -corse-osm;42.1666513166623,9.00361947242636;42.4682025083341,8.66486343656899;41.5243050086871,9.17988557741279;;fastest;polyline;true;true -corse-osm;42.3690578503627,9.04352678218158;42.2568490340933,9.44058851207607;;car;fastest;polyline;true;true -corse-osm;41.7078604440065,9.36320603026543;41.452086844258,9.27340428662486;41.4106993408641,9.23118901771959|41.5776313054049,8.92804898730246;;fastest;polyline;true;true -corse-osm;41.5173095955746,8.97918169950368;42.3306093888311,9.06481667523738;;;fastest;polyline;true;true -corse-osm;41.6666503222939,8.74630874421447;42.2842764702672,8.82059491696069;41.5447278332477,9.4502070946293|41.9653323202627,9.43658244673163;car;;polyline;true;true -corse-osm;42.2551984808268,9.03867767836666;42.5227550248848,8.72837697610259;;car;;polyline;true;true -corse-osm;42.506083633448,8.82026979235467;42.3646928772656,9.08748776409775;;;;polyline;true;true -corse-osm;41.9003083875682,8.66257920836564;41.4584874248691,9.4635921856924;;;;polyline;true;true -corse-osm;41.9555506897019,8.68532789486228;41.946342536225,9.05973404563498;;;;polyline;true;true -corse-osm;41.9176718784869,8.7010508329235;41.4314189526392,8.66987570240162;41.5795820110664,9.312642352283|41.934920910662,9.50605613883352;;;polyline;true;true -corse-osm;41.8445159427682,8.89615235476755;42.082487929929,9.25706393969012;;car;fastest;polyline;true;true -corse-osm;42.3572007760731,9.24177032827865;42.2108392546605,8.75812957913149;;;;polyline;true;true -corse-osm;41.6849748234777,9.32622428155271;42.1913173122285,9.40794291629456;;car;;polyline;true;true -corse-osm;42.2105902813002,8.6446172799333;41.5447278332477,9.4502070946293;;car;;polyline;true;true -corse-osm;41.858134907037,9.20794566819444;41.8056710250862,9.43171103302157;;;fastest;polyline;true;true -corse-osm;41.9084671810875,9.25054778496502;42.5666334762098,9.04389483707957;;;fastest;polyline;true;true -corse-osm;41.4660151358461,8.8860222974699;41.5249742140342,9.37807809574529;42.435111695868,9.41587828371441|41.5447278332477,9.4502070946293;;fastest;polyline;true;true -corse-osm;41.4551911537768,8.80163558300817;41.4877674316568,9.10140649212291;42.152276415322,9.01439847547561;car;fastest;polyline;true;true -corse-osm;41.403155911793,9.17498027076479;41.8687981342059,9.0173717990634;42.1913173122285,9.40794291629456|42.5123430562858,8.77329099609284;car;fastest;polyline;true;true -corse-osm;41.5289915177179,9.30639446315123;41.8208918450773,8.84050445493543;;car;;polyline;true;true -corse-osm;41.6874637901364,9.35961340215057;41.4251622726209,9.25125729649793;;;fastest;polyline;true;true -corse-osm;42.1648399454355,8.80847324477043;41.6825082851062,9.21949539138935;41.8295057986397,9.07088365096599;car;fastest;polyline;true;true -corse-osm;42.3936194989178,9.29247102554887;42.0587649330869,9.05149065919686;41.5988790655928,9.25546265950426|42.4961663060402,9.05523221762618;car;;polyline;true;true -corse-osm;42.5214857479557,8.93552818699973;41.944711162243,9.09422703535994;42.435111695868,9.41587828371441;;fastest;polyline;true;true -corse-osm;42.2634152425406,8.89967834537383;41.6707154264813,9.01356327175163;;car;fastest;polyline;true;true -corse-osm;42.4411236823583,9.4519938291423;42.5189796673646,8.70935858873883;;;fastest;polyline;true;true -corse-osm;41.4106993408641,9.23118901771959;41.8295057986397,9.07088365096599;41.7158439768711,8.83304398993961;car;;polyline;true;true -corse-osm;41.8284015439451,9.18656722351909;41.6070336664841,9.18811432216782;41.8017727926048,9.4208018352068;;;polyline;true;true -corse-osm;42.2733301856043,8.74593808557372;42.4888796936115,9.40771683264757;;car;;polyline;true;true -corse-osm;42.4757081105839,9.00439814660465;42.102765710582,8.85466590945842;;car;;polyline;true;true -corse-osm;42.5111556108762,9.19636671333108;41.6295969960652,8.98008235168876;;;fastest;polyline;true;true -corse-osm;41.6479650687659,9.17928158494411;41.934920910662,9.50605613883352;41.5443718961766,8.81483415379655|41.7181254419638,8.83353409022093;car;fastest;polyline;true;true -corse-osm;41.9834078009333,9.19273838646011;41.644861626178,8.69926636145683;;car;fastest;polyline;true;true -corse-osm;42.5287861881033,8.81088402156718;41.4308762018941,8.73917361909058;;car;fastest;polyline;true;true -corse-osm;41.9441022483632,8.8211155277933;41.4099069674127,9.22870442251675;;car;;polyline;true;true -corse-osm;41.7924136599246,8.9339339953335;42.1354349009041,8.65786910869181;;car;;polyline;true;true -corse-osm;41.8017727926048,9.4208018352068;41.5988790655928,9.25546265950426;41.6805704825697,9.08087568214163;;fastest;polyline;true;true -corse-osm;42.3305276056007,8.70194345247;41.6439420561772,8.64062120559625;41.4251622726209,9.25125729649793|41.8772974060429,9.33969355768524;;;polyline;true;true -corse-osm;41.5086117511056,8.82709097801242;42.2167505581444,9.07964677869808;41.8295057986397,9.07088365096599|42.1330393094104,9.00609113303013;car;fastest;polyline;true;true -corse-osm;41.901051981952,9.47062301342841;41.9366499936115,9.20706611588132;;;fastest;polyline;true;true -corse-osm;41.8001302840142,8.72909356556134;42.4864402093366,9.14811788067687;;;;polyline;true;true -corse-osm;42.349353075549,8.63202592339832;42.6074890049919,9.27597146087792;;car;;polyline;true;true -corse-osm;41.8772974060429,9.33969355768524;42.4699638921302,8.96327671280829;;;;polyline;true;true -corse-osm;41.9525726294145,9.37636758358218;42.4308665731316,9.01329457859276;41.6195584457507,8.8770418198267|41.5443718961766,8.81483415379655;car;fastest;polyline;true;true -corse-osm;42.4682025083341,8.66486343656899;42.5077289510425,9.11789920960087;;;;polyline;true;true -corse-osm;41.9947181914747,9.09560710758669;42.0291180216428,9.40954979021568;;;;polyline;true;true -corse-osm;42.2568490340933,9.44058851207607;41.916848609685,9.4602077349578;;car;;polyline;true;true -corse-osm;41.6495313064661,8.98924457477173;42.4952977301553,9.22894780667731;;;fastest;polyline;true;true -corse-osm;41.452086844258,9.27340428662486;41.46966379249,8.8737880191789;;;;polyline;true;true -corse-osm;42.2822493783897,8.78145097695291;42.1267964139348,8.74122072576079;;;;polyline;true;true -corse-osm;42.3306093888311,9.06481667523738;42.5387388469232,9.2439115669555;;car;fastest;polyline;true;true -corse-osm;41.5012060890626,8.95517995507689;41.7282898358814,8.69446236072108;42.0174223158089,9.17767921910621;;fastest;polyline;true;true -corse-osm;42.2842764702672,8.82059491696069;41.5135319576738,8.704888927727;;;fastest;polyline;true;true -corse-osm;41.958078510887,9.48947655160678;42.3546094470844,9.01441730997059;;car;fastest;polyline;true;true -corse-osm;42.5227550248848,8.72837697610259;42.3999119296111,8.77976285032462;;;fastest;polyline;true;true -corse-osm;42.2476443565777,9.45852444905788;42.152276415322,9.01439847547561;;;;polyline;true;true -corse-osm;42.3646928772656,9.08748776409775;42.3625371469837,8.65749502528924;41.858134907037,9.20794566819444;car;fastest;polyline;true;true diff --git a/test/load/gatling/user-files/resources/road2_parameters_idf_pgr_wo_int.ssv b/test/load/gatling/user-files/resources/road2_parameters_idf_pgr_wo_int.ssv deleted file mode 100644 index c0df906..0000000 --- a/test/load/gatling/user-files/resources/road2_parameters_idf_pgr_wo_int.ssv +++ /dev/null @@ -1,323 +0,0 @@ -resource;start;end;intermediates;profile;optimization;geometryFormat;getSteps;getBbox -bduni-idf-pgr;2.44302401952445,48.4137148207752;2.7731701053679,48.868175015063;;;fastest;polyline;true;true -bduni-idf-pgr;2.7731701053679,48.868175015063;2.56277133412659,48.6960634629475;;;fastest;polyline;true;true -bduni-idf-pgr;2.35450194738805,49.0751770148287;1.85215365886688,48.4394219123526;;car;;polyline;true;true -bduni-idf-pgr;2.56277133412659,48.6960634629475;1.74224134907126,48.9479846086819;;car;;polyline;true;true -bduni-idf-pgr;2.23373788073659,48.6410320689902;2.62949723191559,48.6433561834972;;car;;polyline;true;true -bduni-idf-pgr;1.85215365886688,48.4394219123526;1.77042682394385,48.6283194552176;;car;;polyline;true;true -bduni-idf-pgr;2.63650365769863,49.0903410648694;3.14853146634996,48.9014406567905;;;;polyline;true;true -bduni-idf-pgr;1.74224134907126,48.9479846086819;2.08896000832319,49.0502271274105;;car;fastest;polyline;true;true -bduni-idf-pgr;1.97092077694833,48.887046989752;2.41960498057306,49.0230359021341;;car;fastest;polyline;true;true -bduni-idf-pgr;2.62949723191559,48.6433561834972;3.03651972003281,48.6488741465611;;;;polyline;true;true -bduni-idf-pgr;2.33698104396462,48.5964256068459;1.72928794324398,48.8457934149308;;;fastest;polyline;true;true -bduni-idf-pgr;1.77042682394385,48.6283194552176;2.29819886684418,48.839445828693;;;fastest;polyline;true;true -bduni-idf-pgr;2.15232158452272,48.7187696998008;1.94806619845331,48.4511461691232;;;;polyline;true;true -bduni-idf-pgr;3.14853146634996,48.9014406567905;3.05042917504907,48.5749592368724;;car;;polyline;true;true -bduni-idf-pgr;3.17251162007451,48.6573443401372;2.33938915580511,49.036685355613;;;;polyline;true;true -bduni-idf-pgr;2.08896000832319,49.0502271274105;2.82994266860187,49.0329269678332;;car;;polyline;true;true -bduni-idf-pgr;2.68978281058371,48.8112607997376;3.02433839887381,49.0986915396061;;car;;polyline;true;true -bduni-idf-pgr;2.41960498057306,49.0230359021341;2.61582909338176,48.7528171760961;;car;fastest;polyline;true;true -bduni-idf-pgr;3.07776795178652,49.0640629309462;3.29302724190056,48.4269115800736;;;;polyline;true;true -bduni-idf-pgr;3.03651972003281,48.6488741465611;2.9313952114433,49.0990003583254;;;;polyline;true;true -bduni-idf-pgr;1.71925774142146,48.551671803114;1.9602685533464,49.0086243897676;;car;;polyline;true;true -bduni-idf-pgr;1.72928794324398,48.8457934149308;1.87770117819309,48.959950564173;;car;fastest;polyline;true;true -bduni-idf-pgr;2.28236234672368,48.561669227411;2.80005635507405,48.6121139103547;;;;polyline;true;true -bduni-idf-pgr;2.29819886684418,48.839445828693;1.80565406046808,48.8567842453951;;;;polyline;true;true -bduni-idf-pgr;2.65340593419969,48.5115093970904;2.22147331535816,48.7309725905536;;car;;polyline;true;true -bduni-idf-pgr;1.94806619845331,48.4511461691232;2.21968059018254,48.9512196605792;;;;polyline;true;true -bduni-idf-pgr;1.79766307622194,49.0639751890674;2.04447524771094,48.604622562998;;;fastest;polyline;true;true -bduni-idf-pgr;3.05042917504907,48.5749592368724;2.7201017331332,49.03883580931;;car;fastest;polyline;true;true -bduni-idf-pgr;2.94450366199017,49.0480559564894;2.92997411340475,48.7645577440504;;car;fastest;polyline;true;true -bduni-idf-pgr;2.33938915580511,49.036685355613;3.13090001307428,49.0978166022105;;car;;polyline;true;true -bduni-idf-pgr;3.00574745200574,48.6070149338106;2.14300098456442,48.4424930648878;;;fastest;polyline;true;true -bduni-idf-pgr;2.82994266860187,49.0329269678332;3.03060300946236,48.9548852890963;;;fastest;polyline;true;true -bduni-idf-pgr;1.75955604799092,48.7252603389323;3.02805497646332,48.7392871859018;;;fastest;polyline;true;true -bduni-idf-pgr;3.02433839887381,49.0986915396061;3.2080610755831,48.9742621842306;;car;fastest;polyline;true;true -bduni-idf-pgr;1.86801247894764,48.5516593190609;1.89905630238354,48.9370307860896;;;fastest;polyline;true;true -bduni-idf-pgr;2.61582909338176,48.7528171760961;2.74377198666334,48.5289590148954;;;;polyline;true;true -bduni-idf-pgr;3.2478683128953,49.0825153477024;2.52931407950819,48.82735147723;;car;;polyline;true;true -bduni-idf-pgr;3.29302724190056,48.4269115800736;2.52194954827428,48.7472926355898;;;;polyline;true;true -bduni-idf-pgr;1.74633691161871,49.0965433740057;2.00636674277484,48.590260257991;;;fastest;polyline;true;true -bduni-idf-pgr;2.9313952114433,49.0990003583254;2.77043087631464,48.698779434571;;;fastest;polyline;true;true -bduni-idf-pgr;3.10948592796922,49.0504662042484;2.08577245622873,48.5230459463317;;;;polyline;true;true -bduni-idf-pgr;1.9602685533464,49.0086243897676;3.00240580327809,49.011906299321;;car;fastest;polyline;true;true -bduni-idf-pgr;2.28719979822636,48.7777114449535;2.51701880581677,48.4398246585857;;car;;polyline;true;true -bduni-idf-pgr;1.87770117819309,48.959950564173;3.13924142792821,48.7964165377198;;;fastest;polyline;true;true -bduni-idf-pgr;2.85333677828312,48.6062430019956;1.85347410105169,48.5623292586068;;;;polyline;true;true -bduni-idf-pgr;2.80005635507405,48.6121139103547;1.71951768808067,48.4922047724947;;car;;polyline;true;true -bduni-idf-pgr;2.86846008636057,48.8194918020628;2.65721126645803,49.0341246748343;;car;fastest;polyline;true;true -bduni-idf-pgr;1.80565406046808,48.8567842453951;3.15767398253083,48.4921172051691;;car;;polyline;true;true -bduni-idf-pgr;2.21233152970672,49.0853195460746;1.74986352920532,48.4846012684749;;car;fastest;polyline;true;true -bduni-idf-pgr;2.22147331535816,48.7309725905536;3.24412962421775,48.5043474580627;;;;polyline;true;true -bduni-idf-pgr;2.46903090253472,48.7825470240787;2.24155541658401,48.924730171659;;car;;polyline;true;true -bduni-idf-pgr;2.21968059018254,48.9512196605792;2.07961494810879,48.4085965128383;;;;polyline;true;true -bduni-idf-pgr;2.15995734296739,48.6497260325123;3.08779237866402,48.9104905051412;;;;polyline;true;true -bduni-idf-pgr;2.04447524771094,48.604622562998;2.26028655618429,48.65906533706;;car;;polyline;true;true -bduni-idf-pgr;2.63795228265226,48.470772205526;2.10360035635531,48.7667713400209;;car;;polyline;true;true -bduni-idf-pgr;2.7201017331332,49.03883580931;2.92441692948341,49.0818783620838;;car;;polyline;true;true -bduni-idf-pgr;3.24241158999503,48.5104270996992;2.68016149103642,48.9174412503606;;;fastest;polyline;true;true -bduni-idf-pgr;2.92997411340475,48.7645577440504;2.25455458052456,48.5617117720889;;;fastest;polyline;true;true -bduni-idf-pgr;3.10586747080088,48.8151862436673;2.11147376447916,48.7175056818407;;;;polyline;true;true -bduni-idf-pgr;3.13090001307428,49.0978166022105;3.06813656724989,49.0666487353155;;;;polyline;true;true -bduni-idf-pgr;1.75176373198628,48.6717312326888;2.97977443821728,48.5418979424285;;car;;polyline;true;true -bduni-idf-pgr;2.14300098456442,48.4424930648878;1.77868135385215,48.6940694178222;;;fastest;polyline;true;true -bduni-idf-pgr;1.77052700333297,48.5874861966819;2.28014034964144,48.4508814013563;;;fastest;polyline;true;true -bduni-idf-pgr;3.03060300946236,48.9548852890963;2.16519124992192,48.4554131807759;;car;fastest;polyline;true;true -bduni-idf-pgr;1.82130594663322,48.9569179091603;2.56359329149127,49.0558625487611;;car;fastest;polyline;true;true -bduni-idf-pgr;3.02805497646332,48.7392871859018;2.20090577974915,48.8656499942997;;;;polyline;true;true -bduni-idf-pgr;1.77758952975273,48.8166411499027;1.81617962121964,49.029776561656;;;;polyline;true;true -bduni-idf-pgr;3.2080610755831,48.9742621842306;2.3203807298094,49.0524362850701;;car;;polyline;true;true -bduni-idf-pgr;1.79864390790462,48.8007683404721;2.40034448355436,49.0499207292916;;;fastest;polyline;true;true -bduni-idf-pgr;1.89905630238354,48.9370307860896;3.01475152149796,48.7522216537734;;;fastest;polyline;true;true -bduni-idf-pgr;2.80626571998,48.7127389817731;2.9590084515512,48.7590084518772;;;fastest;polyline;true;true -bduni-idf-pgr;2.74377198666334,48.5289590148954;2.66359091699123,48.5864080399973;;car;fastest;polyline;true;true -bduni-idf-pgr;2.62659822516143,49.0045754485996;2.69223699234426,48.781094395183;;car;fastest;polyline;true;true -bduni-idf-pgr;2.52931407950819,48.82735147723;1.84350316822529,48.7271079975413;;car;fastest;polyline;true;true -bduni-idf-pgr;2.92537104487419,48.4460248626536;2.06936389505863,48.4106162068434;;car;;polyline;true;true -bduni-idf-pgr;2.52194954827428,48.7472926355898;2.12147863954306,48.6167812872445;;car;fastest;polyline;true;true -bduni-idf-pgr;3.00405092947185,48.928900136007;3.05554540157318,48.9368265053024;;car;fastest;polyline;true;true -bduni-idf-pgr;2.00636674277484,48.590260257991;1.72362964600325,48.8008628757438;;;;polyline;true;true -bduni-idf-pgr;2.44998060241342,48.9409831087571;2.58977488055825,48.5484650451224;;;fastest;polyline;true;true -bduni-idf-pgr;2.77043087631464,48.698779434571;2.85722216144204,48.8624128093012;;;;polyline;true;true -bduni-idf-pgr;2.36744628064334,48.5593186914921;2.24830394499004,48.447400964587;;car;;polyline;true;true -bduni-idf-pgr;2.08577245622873,48.5230459463317;1.89837341569364,48.5615852880524;;;fastest;polyline;true;true -bduni-idf-pgr;2.97855033129454,48.4272446077317;1.86097037009895,48.9765269104158;;;fastest;polyline;true;true -bduni-idf-pgr;3.00240580327809,49.011906299321;1.77136054523289,48.5881791703403;;;;polyline;true;true -bduni-idf-pgr;2.01686525307596,48.559019746189;2.92506045550108,48.8759416235844;;car;fastest;polyline;true;true -bduni-idf-pgr;2.51701880581677,48.4398246585857;1.82099920958281,48.9100377754308;;;;polyline;true;true -bduni-idf-pgr;2.54839190617204,48.508401370002;2.26064089313149,48.600889604562;;;;polyline;true;true -bduni-idf-pgr;3.13924142792821,48.7964165377198;2.33598023504019,48.9742363027763;;car;fastest;polyline;true;true -bduni-idf-pgr;3.1219716783613,48.9284332596697;1.93459122255445,48.9302670808276;;;;polyline;true;true -bduni-idf-pgr;1.85347410105169,48.5623292586068;1.87565202154219,48.8267301582033;;car;fastest;polyline;true;true -bduni-idf-pgr;2.2769722353667,49.0718624827452;2.48952550180256,48.8390767888166;;;fastest;polyline;true;true -bduni-idf-pgr;1.71951768808067,48.4922047724947;2.371133909747,48.6451014467282;;;;polyline;true;true -bduni-idf-pgr;2.27931108362973,48.5834542725701;1.78422989808023,48.6993033653358;;;;polyline;true;true -bduni-idf-pgr;2.65721126645803,49.0341246748343;2.4685234580189,48.4883759838995;;;fastest;polyline;true;true -bduni-idf-pgr;1.72670273520052,48.6091325195273;3.18409432508051,48.6388877378544;;car;fastest;polyline;true;true -bduni-idf-pgr;3.15767398253083,48.4921172051691;2.56231252923608,48.6639785184991;;;fastest;polyline;true;true -bduni-idf-pgr;2.33809377290308,49.0662992510945;2.64215860851109,48.984148150729;;car;;polyline;true;true -bduni-idf-pgr;1.74986352920532,48.4846012684749;3.15085980184376,48.600839496241;;;fastest;polyline;true;true -bduni-idf-pgr;1.88929816000164,48.7471217040671;1.97713369987905,48.791406722297;;car;;polyline;true;true -bduni-idf-pgr;3.24412962421775,48.5043474580627;1.82923176512122,48.8328967497917;;car;;polyline;true;true -bduni-idf-pgr;2.80613363981247,48.5143222743878;3.28460853025317,48.4831821521511;;car;fastest;polyline;true;true -bduni-idf-pgr;2.24155541658401,48.924730171659;3.22628464661539,48.9242739787558;;;fastest;polyline;true;true -bduni-idf-pgr;2.49616491571069,49.0556509082206;2.61654628627002,48.5710180676775;;;fastest;polyline;true;true -bduni-idf-pgr;2.07961494810879,48.4085965128383;2.37246924117207,48.7314217333682;;car;;polyline;true;true -bduni-idf-pgr;2.42679364904761,48.7121110518929;3.23969852700829,48.535220417357;;;;polyline;true;true -bduni-idf-pgr;3.08779237866402,48.9104905051412;1.96024861447513,49.056108039245;;car;;polyline;true;true -bduni-idf-pgr;2.96242040134966,48.9512265204685;3.155211642012,48.4280089232605;;car;;polyline;true;true -bduni-idf-pgr;2.26028655618429,48.65906533706;3.23662505336106,48.5814448561752;;;fastest;polyline;true;true -bduni-idf-pgr;2.36061288639903,48.6042591482634;2.89913119934499,48.5183612552704;;car;;polyline;true;true -bduni-idf-pgr;2.10360035635531,48.7667713400209;2.61894542127848,48.8922312215669;;car;;polyline;true;true -bduni-idf-pgr;1.81379353366792,48.6845802163007;2.59792239181697,48.9980786484433;;car;fastest;polyline;true;true -bduni-idf-pgr;2.92441692948341,49.0818783620838;1.76166519001126,48.6214782817755;;car;;polyline;true;true -bduni-idf-pgr;2.28350601159036,49.0320976303658;3.13369113653898,48.4495781086152;;;fastest;polyline;true;true -bduni-idf-pgr;2.68016149103642,48.9174412503606;3.27710180804133,48.8345374118537;;car;;polyline;true;true -bduni-idf-pgr;2.6395819529891,48.7474210214568;2.8658278092742,48.8326704449952;;car;;polyline;true;true -bduni-idf-pgr;2.25455458052456,48.5617117720889;2.03776987977326,48.7019950303948;;;fastest;polyline;true;true -bduni-idf-pgr;2.38692319877446,48.8723046395462;2.92958007454872,48.7282504305243;;car;;polyline;true;true -bduni-idf-pgr;2.11147376447916,48.7175056818407;2.69896444119513,49.0269234210951;;car;fastest;polyline;true;true -bduni-idf-pgr;1.87514612339437,48.6180083501386;3.09881096854806,48.9550993709359;;;;polyline;true;true -bduni-idf-pgr;3.06813656724989,49.0666487353155;2.63602442666888,48.8933715506224;;car;fastest;polyline;true;true -bduni-idf-pgr;2.6561216879636,48.4137725349516;1.90038941502571,48.4410803312436;;;;polyline;true;true -bduni-idf-pgr;2.97977443821728,48.5418979424285;3.20843671634793,48.6092077212874;;;fastest;polyline;true;true -bduni-idf-pgr;1.80677667967975,48.9877160120523;2.88024089708924,48.8595147127518;;;;polyline;true;true -bduni-idf-pgr;1.77868135385215,48.6940694178222;2.31517860740423,48.7657573206117;;;fastest;polyline;true;true -bduni-idf-pgr;2.88020384497941,49.0967500364408;2.44282567165792,49.0601615046151;;car;;polyline;true;true -bduni-idf-pgr;2.28014034964144,48.4508814013563;2.30205286554992,49.0095631321194;;;fastest;polyline;true;true -bduni-idf-pgr;2.98547409772873,48.9869357196148;1.71405324935913,48.9862172704889;;;fastest;polyline;true;true -bduni-idf-pgr;2.16519124992192,48.4554131807759;2.60954860933125,48.4981848334428;;car;fastest;polyline;true;true -bduni-idf-pgr;2.1965329580009,48.9146899617976;3.20797933265567,48.8752215392655;;;;polyline;true;true -bduni-idf-pgr;2.56359329149127,49.0558625487611;2.49760761782527,48.9654066073243;;;fastest;polyline;true;true -bduni-idf-pgr;2.02813939489424,48.8874126581941;2.81265951655805,48.8858365442837;;;;polyline;true;true -bduni-idf-pgr;2.20090577974915,48.8656499942997;2.63893944807351,48.6464220805326;;;;polyline;true;true -bduni-idf-pgr;2.25354071334004,48.8318400320131;2.98378565572202,48.4430167715531;;;;polyline;true;true -bduni-idf-pgr;1.81617962121964,49.029776561656;2.21188332661986,48.5665090380236;;car;;polyline;true;true -bduni-idf-pgr;2.17518935017288,49.0241894028615;2.42714392915368,48.8056369547965;;;;polyline;true;true -bduni-idf-pgr;2.3203807298094,49.0524362850701;1.90414913594723,48.8445831454825;;;fastest;polyline;true;true -bduni-idf-pgr;1.83959630317986,48.4131046754308;2.60319691188633,48.6644521122798;;;fastest;polyline;true;true -bduni-idf-pgr;2.40034448355436,49.0499207292916;1.94474933221936,49.0013767180964;;car;fastest;polyline;true;true -bduni-idf-pgr;2.79377773739398,48.4947809746489;1.90096779540181,48.7199568069773;;car;;polyline;true;true -bduni-idf-pgr;3.01475152149796,48.7522216537734;1.98849392086267,48.7762989106355;;car;;polyline;true;true -bduni-idf-pgr;2.30098935961723,48.7266279263888;3.17967119179666,49.033109962428;;car;;polyline;true;true -bduni-idf-pgr;2.9590084515512,48.7590084518772;2.37337832003832,49.0185763883637;;car;;polyline;true;true -bduni-idf-pgr;2.48865631446242,48.8171713173157;2.66718358658254,48.6502973634982;;;fastest;polyline;true;true -bduni-idf-pgr;2.66359091699123,48.5864080399973;2.65378841906786,48.8530607349938;;car;;polyline;true;true -bduni-idf-pgr;3.02200339883566,48.8232395383529;1.75942298993468,48.6627617059974;;;fastest;polyline;true;true -bduni-idf-pgr;2.69223699234426,48.781094395183;1.78632455803454,48.9274080703268;;car;fastest;polyline;true;true -bduni-idf-pgr;2.46888927929103,48.6387730957242;2.34170696213841,48.6132798478706;;car;fastest;polyline;true;true -bduni-idf-pgr;1.84350316822529,48.7271079975413;3.04588063806295,48.4732514865231;;car;;polyline;true;true -bduni-idf-pgr;2.19786168299615,48.8458025997505;2.47313884682953,49.0847438615281;;;fastest;polyline;true;true -bduni-idf-pgr;2.06936389505863,48.4106162068434;2.98047252073884,48.4200417489512;;;;polyline;true;true -bduni-idf-pgr;1.82886183969676,48.9577273936942;2.90867525599897,48.577031161054;;car;;polyline;true;true -bduni-idf-pgr;2.12147863954306,48.6167812872445;1.87185139842331,48.5202892370056;;;;polyline;true;true -bduni-idf-pgr;2.96994536928833,49.0740880992496;2.72634820900857,48.9121631982736;;car;fastest;polyline;true;true -bduni-idf-pgr;3.05554540157318,48.9368265053024;3.25897775925696,48.947050251835;;car;fastest;polyline;true;true -bduni-idf-pgr;2.81734802238643,48.6399777745828;1.95096476227045,48.6524713821942;;;;polyline;true;true -bduni-idf-pgr;1.72362964600325,48.8008628757438;2.08726699501276,48.4177114662481;;;fastest;polyline;true;true -bduni-idf-pgr;2.43606786131859,48.6523412133101;3.00724473297596,48.5413840666413;;car;;polyline;true;true -bduni-idf-pgr;2.58977488055825,48.5484650451224;3.12126296684146,49.0275092432508;;car;;polyline;true;true -bduni-idf-pgr;2.81350911594927,49.053426356311;3.16698793284595,49.0059921407374;;;fastest;polyline;true;true -bduni-idf-pgr;2.85722216144204,48.8624128093012;3.18716344460845,48.8380181001034;;;;polyline;true;true -bduni-idf-pgr;1.71817801259458,48.9931880616117;3.03308020718396,48.8012332331622;;car;fastest;polyline;true;true -bduni-idf-pgr;2.24830394499004,48.447400964587;3.01740132682025,48.9715511559974;;;;polyline;true;true -bduni-idf-pgr;2.12365882359445,48.5913832220249;2.90906613878906,48.8651268957183;;car;fastest;polyline;true;true -bduni-idf-pgr;1.89837341569364,48.5615852880524;2.14403748773038,48.6982124962611;;car;;polyline;true;true -bduni-idf-pgr;2.9955084014684,48.6209766281303;2.29415314942598,48.7855380748166;;car;fastest;polyline;true;true -bduni-idf-pgr;1.86097037009895,48.9765269104158;1.77023191601038,48.835192236281;;;fastest;polyline;true;true -bduni-idf-pgr;2.20131517611444,48.5984010657296;2.80260041020811,49.0229929501656;;;;polyline;true;true -bduni-idf-pgr;1.77136054523289,48.5881791703403;2.62434866689146,48.7142322225496;;car;fastest;polyline;true;true -bduni-idf-pgr;1.9883840225637,49.0811516144313;2.51490940265357,48.7803513203748;;;;polyline;true;true -bduni-idf-pgr;2.92506045550108,48.8759416235844;2.55255498997867,48.9194628409808;;car;fastest;polyline;true;true -bduni-idf-pgr;2.18449685834348,48.8928286401788;1.7239506047219,48.7769301553955;;;;polyline;true;true -bduni-idf-pgr;1.82099920958281,48.9100377754308;2.62849176079035,48.9117487736046;;;;polyline;true;true -bduni-idf-pgr;3.08201187215745,48.9602073112968;2.42771309092641,48.6845061585074;;;;polyline;true;true -bduni-idf-pgr;2.26064089313149,48.600889604562;2.2642200101167,48.7073045867961;;;fastest;polyline;true;true -bduni-idf-pgr;2.95409759096801,48.4892538063927;3.24920990467072,48.7504414305324;;;fastest;polyline;true;true -bduni-idf-pgr;2.33598023504019,48.9742363027763;2.65315005257726,49.0268406333635;;car;fastest;polyline;true;true -bduni-idf-pgr;3.07744960524142,48.9785587020451;3.06247456669807,48.4134095962858;;;fastest;polyline;true;true -bduni-idf-pgr;1.93459122255445,48.9302670808276;2.70311385728419,48.6855158704799;;car;fastest;polyline;true;true -bduni-idf-pgr;2.73912299089134,48.4744705524063;2.33896833099425,48.7248882452725;;;;polyline;true;true -bduni-idf-pgr;1.87565202154219,48.8267301582033;1.93512160927057,48.6344014046714;;;;polyline;true;true -bduni-idf-pgr;1.77118125297129,49.0274510557763;2.63065129593015,48.7589763323078;;car;;polyline;true;true -bduni-idf-pgr;2.48952550180256,48.8390767888166;2.61563961282372,49.0248309108894;;car;fastest;polyline;true;true -bduni-idf-pgr;1.78129711225629,48.7091508675599;2.35468277707696,48.5030348944478;;;;polyline;true;true -bduni-idf-pgr;2.371133909747,48.6451014467282;3.17790134847164,48.8820506641408;;car;fastest;polyline;true;true -bduni-idf-pgr;2.80124563872814,48.6732986173127;2.79613966569304,48.5741212349851;;;fastest;polyline;true;true -bduni-idf-pgr;1.78422989808023,48.6993033653358;1.77025827243924,48.8180651763687;;car;;polyline;true;true -bduni-idf-pgr;2.91238521821797,48.9932538401103;2.3657249815762,48.5469554207986;;;;polyline;true;true -bduni-idf-pgr;2.4685234580189,48.4883759838995;2.47266492471099,48.7841440092307;;car;;polyline;true;true -bduni-idf-pgr;2.65210498459637,48.5873672699323;2.14741960763931,49.0060348907718;;;;polyline;true;true -bduni-idf-pgr;3.18409432508051,48.6388877378544;2.30891627632082,48.4137112971162;;;;polyline;true;true -bduni-idf-pgr;2.93746135793626,48.846178816841;2.13674341440201,48.6730940852081;;;;polyline;true;true -bduni-idf-pgr;2.56231252923608,48.6639785184991;2.88402340710163,48.4173302368261;;;fastest;polyline;true;true -bduni-idf-pgr;2.8157651361078,48.5403529864969;2.7808874193579,48.6516721581575;;;fastest;polyline;true;true -bduni-idf-pgr;2.64215860851109,48.984148150729;2.53900687582791,49.0219269554829;;car;fastest;polyline;true;true -bduni-idf-pgr;2.72624706290662,48.7940942401299;2.92554791979492,48.7662857511314;;car;;polyline;true;true -bduni-idf-pgr;3.15085980184376,48.600839496241;2.61457718908787,48.7606833498226;;car;;polyline;true;true -bduni-idf-pgr;2.34993697851896,49.0004024068359;2.44997046217322,48.7542010897305;;car;;polyline;true;true -bduni-idf-pgr;1.97713369987905,48.791406722297;2.69174239560962,48.5570644505555;;car;fastest;polyline;true;true -bduni-idf-pgr;2.21946005299687,48.9128135661362;2.40341152437031,48.8191350154579;;;fastest;polyline;true;true -bduni-idf-pgr;1.82923176512122,48.8328967497917;2.42162180915475,49.0414450021693;;;;polyline;true;true -bduni-idf-pgr;2.13496898785233,48.9560354938498;2.39156349077821,48.5970387435518;;;fastest;polyline;true;true -bduni-idf-pgr;3.28460853025317,48.4831821521511;1.97832168713212,48.8806318745948;;car;fastest;polyline;true;true -bduni-idf-pgr;2.77608941085637,48.678063773131;2.22452594041824,48.7712506627198;;;;polyline;true;true -bduni-idf-pgr;3.22628464661539,48.9242739787558;2.411614953354,48.6086867272854;;car;fastest;polyline;true;true -bduni-idf-pgr;3.16368755064905,48.8144462611061;2.51069469451904,48.5629805336241;;car;;polyline;true;true -bduni-idf-pgr;2.61654628627002,48.5710180676775;1.84811977297068,48.5118584437063;;car;fastest;polyline;true;true -bduni-idf-pgr;1.86342695392668,48.5766375748673;2.81744394339621,48.6286925512133;;car;fastest;polyline;true;true -bduni-idf-pgr;2.37246924117207,48.7314217333682;1.77044644728303,48.835936860228;;car;;polyline;true;true -bduni-idf-pgr;2.13166442662477,48.5503719745437;2.18552004061639,48.5676715224516;;;;polyline;true;true -bduni-idf-pgr;3.23969852700829,48.535220417357;2.07531846836209,48.4924617862562;;car;;polyline;true;true -bduni-idf-pgr;3.17785889729857,49.0330999343423;2.74112369641662,48.6395581742749;;car;;polyline;true;true -bduni-idf-pgr;1.96024861447513,49.056108039245;3.00984625890851,48.936620601383;;;fastest;polyline;true;true -bduni-idf-pgr;1.88465708866715,48.4449474926805;3.24550998099148,48.8833222137997;;car;;polyline;true;true -bduni-idf-pgr;3.155211642012,48.4280089232605;2.96623665466905,48.4148942076834;;;fastest;polyline;true;true -bduni-idf-pgr;3.00523122362792,48.9940215313109;2.42308107987046,48.6953704123618;;;;polyline;true;true -bduni-idf-pgr;3.23662505336106,48.5814448561752;3.17843544334173,48.6447293995181;;car;;polyline;true;true -bduni-idf-pgr;1.7113136716187,48.553526684246;3.06107096299529,49.0866241728189;;car;fastest;polyline;true;true -bduni-idf-pgr;2.89913119934499,48.5183612552704;1.70145761668682,48.5891884437297;;;fastest;polyline;true;true -bduni-idf-pgr;2.70785078518093,48.507257003407;2.17305626124144,48.4751185767585;;;fastest;polyline;true;true -bduni-idf-pgr;2.61894542127848,48.8922312215669;2.12264413237572,48.6806732026394;;;;polyline;true;true -bduni-idf-pgr;1.78459358438849,48.4057517669629;3.27955346107483,48.6691871895455;;;fastest;polyline;true;true -bduni-idf-pgr;2.59792239181697,48.9980786484433;2.77620098814368,48.4779397946317;;;fastest;polyline;true;true -bduni-idf-pgr;2.53806917257607,48.7204011785099;2.79147772714496,48.7053473231615;;car;;polyline;true;true -bduni-idf-pgr;1.76166519001126,48.6214782817755;2.25704414881766,48.5834885267075;;car;fastest;polyline;true;true -bduni-idf-pgr;3.21513230241835,48.636160280765;3.00117604546249,48.5279869676102;;;fastest;polyline;true;true -bduni-idf-pgr;3.13369113653898,48.4495781086152;2.51610577963293,48.6193799407454;;car;;polyline;true;true -bduni-idf-pgr;1.70224213190377,48.5667912617559;2.60930366925895,48.5994060098426;;car;fastest;polyline;true;true -bduni-idf-pgr;3.27710180804133,48.8345374118537;2.1088197439909,49.0329182558227;;;fastest;polyline;true;true -bduni-idf-pgr;1.93520829193294,48.5822563250083;2.97879804186523,49.0418382084463;;car;;polyline;true;true -bduni-idf-pgr;2.8658278092742,48.8326704449952;1.74143002107739,48.6502963735489;;;;polyline;true;true -bduni-idf-pgr;3.14830018132925,48.4963647234254;2.36237132549286,49.0024718885776;;car;fastest;polyline;true;true -bduni-idf-pgr;2.03776987977326,48.7019950303948;2.27105385474861,48.4473506370559;;;fastest;polyline;true;true -bduni-idf-pgr;2.77452237308025,48.5603685131995;3.19282997846603,48.4008056103019;;car;;polyline;true;true -bduni-idf-pgr;2.92958007454872,48.7282504305243;3.2312304135412,49.0795749890851;;car;fastest;polyline;true;true -bduni-idf-pgr;2.84637838639319,49.009762154636;3.08899837583303,49.0383949865354;;car;;polyline;true;true -bduni-idf-pgr;2.69896444119513,49.0269234210951;3.02624191790819,48.587071515806;;car;fastest;polyline;true;true -bduni-idf-pgr;2.82771084494889,48.7242462720955;1.75015484318137,48.9140947713051;;;fastest;polyline;true;true -bduni-idf-pgr;3.09881096854806,48.9550993709359;2.29008085019886,48.7066363268299;;;fastest;polyline;true;true -bduni-idf-pgr;2.67328545004129,48.8863567294087;2.48435933217406,48.5286500309128;;;fastest;polyline;true;true -bduni-idf-pgr;2.63602442666888,48.8933715506224;2.34159820824862,49.0844875207171;;car;;polyline;true;true -bduni-idf-pgr;1.88642966076732,48.9083162062801;2.85769811905921,48.8256619161693;;car;;polyline;true;true -bduni-idf-pgr;1.90038941502571,48.4410803312436;2.09707062914968,48.816647517751;;;fastest;polyline;true;true -bduni-idf-pgr;1.83894036822021,49.0832666419912;2.69410366714001,48.8021550313104;;;;polyline;true;true -bduni-idf-pgr;3.20843671634793,48.6092077212874;2.55717359632254,48.8056579111144;;;fastest;polyline;true;true -bduni-idf-pgr;3.08813234567642,48.9295926973224;3.22132597714663,48.6693156572292;;;;polyline;true;true -bduni-idf-pgr;2.88024089708924,48.8595147127518;3.06893367916346,48.7707151483744;;car;fastest;polyline;true;true -bduni-idf-pgr;2.70997451581061,48.6047025687993;2.44796397797763,48.6530489895958;;;;polyline;true;true -bduni-idf-pgr;2.31517860740423,48.7657573206117;1.73853508792818,48.7234391525853;;car;;polyline;true;true -bduni-idf-pgr;1.89589908793569,48.7866324947681;2.38785015568137,48.4433182034642;;;fastest;polyline;true;true -bduni-idf-pgr;1.77893240749836,48.4801739969989;2.24767736084759,48.6571313232183;;car;;polyline;true;true -bduni-idf-pgr;2.38250925689936,48.5080187392654;2.29749291017652,48.7354998544091;;car;;polyline;true;true -bduni-idf-pgr;2.75157957784832,48.6696816918906;2.6106046397239,48.6184849328361;;car;;polyline;true;true -bduni-idf-pgr;2.67308412827551,49.0467134487815;1.88871848993003,48.7858450352214;;;;polyline;true;true -bduni-idf-pgr;2.28414396755397,48.5412525953725;2.26862809471786,48.6602929371409;;;;polyline;true;true -bduni-idf-pgr;1.76589241586626,48.6122939452063;2.87847086787224,48.8704217498889;;car;fastest;polyline;true;true -bduni-idf-pgr;3.28558418564498,49.0100721106399;2.61345944292843,48.84184322129;;car;fastest;polyline;true;true -bduni-idf-pgr;2.97836105301976,48.4586890732171;1.86185560561717,48.7619135329966;;car;;polyline;true;true -bduni-idf-pgr;3.05286432802677,48.5488145295065;2.24766672290862,48.8326830349863;;;;polyline;true;true -bduni-idf-pgr;2.29398637488484,48.532341744774;2.03509683050215,49.0130370791303;;;fastest;polyline;true;true -bduni-idf-pgr;3.155302355811,48.8088112379191;1.79400271475315,48.7254965908127;;car;fastest;polyline;true;true -bduni-idf-pgr;2.1394589394331,48.655172704393;3.04455347955227,48.4636280691251;;;;polyline;true;true -bduni-idf-pgr;2.82006553113461,48.5720650034724;2.72786941751838,48.6281849443447;;;;polyline;true;true -bduni-idf-pgr;2.50881831422448,48.6162664430216;2.52380169704556,48.5565654592123;;;;polyline;true;true -bduni-idf-pgr;2.80508779771626,48.8314150076825;3.24237989038229,48.9211644186638;;car;;polyline;true;true -bduni-idf-pgr;2.90736396126449,48.8145677352557;2.95437494143844,48.895003245445;;car;fastest;polyline;true;true -bduni-idf-pgr;1.76483602523804,48.7834289334947;2.44333230108023,48.6851161454339;;car;fastest;polyline;true;true -bduni-idf-pgr;1.74138205721974,48.4951852770289;2.05962646864355,48.7131408974528;;car;;polyline;true;true -bduni-idf-pgr;2.16240077130497,48.9056342708878;2.57546874433756,49.0215309059946;;;;polyline;true;true -bduni-idf-pgr;2.86805079244077,48.9111416435801;1.96211568191648,48.4904934111983;;;fastest;polyline;true;true -bduni-idf-pgr;2.39693894758821,48.6177439929219;2.11159228160977,48.6870462149614;;;fastest;polyline;true;true -bduni-idf-pgr;2.44764026515186,48.7303717983887;1.86678234227002,48.5947005617898;;;fastest;polyline;true;true -bduni-idf-pgr;2.48838028870523,48.4055102229118;2.87733382321894,48.7122184444452;;;;polyline;true;true -bduni-idf-pgr;2.03093082383275,48.8598328259774;1.89327391758561,48.9963914602296;;car;fastest;polyline;true;true -bduni-idf-pgr;2.69736703597009,48.6363132799277;3.182831607759,48.9348469878547;;;fastest;polyline;true;true -bduni-idf-pgr;3.15717746093869,48.4581351812463;2.41245734393597,48.6886572781252;;car;fastest;polyline;true;true -bduni-idf-pgr;2.70299930684268,48.4036925303983;2.43869958035648,48.7360838004854;;;fastest;polyline;true;true -bduni-idf-pgr;2.79737324118614,48.6007891183486;2.99430664107203,48.9486130156089;;car;fastest;polyline;true;true -bduni-idf-pgr;2.50388777069747,48.8045362858102;2.34645159579813,48.8466877503786;;;;polyline;true;true -bduni-idf-pgr;2.34890620075166,48.5734699483961;2.16593897081912,49.0283524840139;;;;polyline;true;true -bduni-idf-pgr;3.12934719100594,48.5202896042028;1.89534109197557,48.5789273653179;;car;fastest;polyline;true;true -bduni-idf-pgr;2.89886239394546,49.0337235135958;2.92045889385045,48.7608956388664;;;;polyline;true;true -bduni-idf-pgr;2.65560555048287,48.6464879555162;2.41393017545342,48.943114944757;;car;;polyline;true;true -bduni-idf-pgr;1.7402398083359,48.7374177104328;2.24627429097891,48.7397860897239;;car;fastest;polyline;true;true -bduni-idf-pgr;2.76597310937941,48.8385955642443;2.44394816793501,48.9053963240003;;;fastest;polyline;true;true -bduni-idf-pgr;3.01715762168169,48.6969391793944;3.20263568758965,48.9626127462136;;car;fastest;polyline;true;true -bduni-idf-pgr;2.54577292092144,48.4032152522588;2.92621581666172,48.8457779747201;;;fastest;polyline;true;true -bduni-idf-pgr;3.2949625082314,48.9339722776553;2.54716102220118,48.4277711126255;;car;;polyline;true;true -bduni-idf-pgr;3.13784370422363,48.4485147462459;3.1811253618449,48.7178375440184;;car;;polyline;true;true -bduni-idf-pgr;2.33329236730933,48.8876486113295;2.14565384984016,48.824415569962;;car;;polyline;true;true -bduni-idf-pgr;2.20525981336832,48.4524710243102;2.98737781271338,48.733678951324;;car;;polyline;true;true -bduni-idf-pgr;1.70351696610451,48.6967224117834;2.19678191952407,48.628333799378;;;;polyline;true;true -bduni-idf-pgr;1.9317928943783,48.9696621822426;2.47072629034519,49.0356613384327;;;fastest;polyline;true;true -bduni-idf-pgr;2.10365320518613,48.9907704428537;3.29506550841033,49.0098599653458;;car;;polyline;true;true -bduni-idf-pgr;1.73959526382387,48.907115195808;2.17901276350021,48.7272703210358;;;;polyline;true;true -bduni-idf-pgr;2.53201073370874,48.7656614233041;2.15153104066849,48.9009692120831;;;;polyline;true;true -bduni-idf-pgr;3.28023196272552,48.7218059241073;3.26586572155356,48.8291404705029;;car;fastest;polyline;true;true -bduni-idf-pgr;2.42753846421838,49.0898653692799;3.2305314540863,48.5943622092018;;car;fastest;polyline;true;true -bduni-idf-pgr;2.06901542656124,48.6345081325853;2.61021210178733,48.8953208891209;;car;fastest;polyline;true;true -bduni-idf-pgr;2.05548841618001,48.606448809104;1.84710865542293,48.8563449443784;;car;fastest;polyline;true;true -bduni-idf-pgr;1.74734552763402,48.8885561947245;3.05129791498184,48.7961439184844;;;fastest;polyline;true;true -bduni-idf-pgr;3.02238834574819,48.5270605054218;1.86672330908477,48.6245439877966;;;fastest;polyline;true;true -bduni-idf-pgr;2.76956245601177,48.6712490440812;2.40666649341583,48.84756819834;;car;;polyline;true;true -bduni-idf-pgr;2.46519443653524,48.4101225436898;2.26161520555615,48.7464743186021;;;;polyline;true;true -bduni-idf-pgr;3.15562987178564,48.5155062379781;3.18331082500517,48.7414217407582;;car;;polyline;true;true -bduni-idf-pgr;2.45104273669422,48.7872313115979;3.26537883169949,48.4060058250558;;;;polyline;true;true -bduni-idf-pgr;2.05847477726638,48.5304783798056;1.78662376813591,48.7435751098674;;car;;polyline;true;true -bduni-idf-pgr;2.44915228784084,48.7143794547766;1.79427379630506,48.5892938267207;;car;;polyline;true;true -bduni-idf-pgr;2.70950169526041,48.7773852509446;1.70392192415893,48.6924740931252;;car;fastest;polyline;true;true -bduni-idf-pgr;2.47131392210722,48.5686107953079;2.47563578039408,48.5232675248291;;;fastest;polyline;true;true -bduni-idf-pgr;3.00200576223433,48.9775314524537;2.61872317120433,48.8471586129162;;car;fastest;polyline;true;true -bduni-idf-pgr;3.23854403235018,48.802837240696;2.01163341850042,48.8532252323348;;car;fastest;polyline;true;true -bduni-idf-pgr;1.94985375031829,48.8609381208662;2.14056989103556,48.937168886303;;car;fastest;polyline;true;true -bduni-idf-pgr;2.98303970173001,48.8078082440421;2.36212318986654,48.6093356524361;;car;;polyline;true;true -bduni-idf-pgr;1.75505315475166,48.7259683815297;2.53619465194643,48.9744010594208;;car;fastest;polyline;true;true -bduni-idf-pgr;2.65726837515831,48.5846856117714;2.85868522003293,48.7052039258881;;car;fastest;polyline;true;true -bduni-idf-pgr;1.91799417063594,48.8083277816419;2.38365162909031,48.8877615066245;;;fastest;polyline;true;true -bduni-idf-pgr;3.06941128447652,48.8862750092056;1.78778480961919,48.7519670638721;;car;fastest;polyline;true;true -bduni-idf-pgr;1.84866771772504,48.7393558819313;2.57951624765992,48.6120211988455;;car;fastest;polyline;true;true -bduni-idf-pgr;2.28624591007829,48.797935621161;1.83973868265748,48.6104129117448;;car;;polyline;true;true -bduni-idf-pgr;2.15655271187425,48.9793275719276;2.96782912127674,48.7476802977268;;;fastest;polyline;true;true -bduni-idf-pgr;2.48459296412766,48.480618487997;2.56370316445827,48.6602818151237;;;;polyline;true;true -bduni-idf-pgr;2.94533781297505,48.97076268401;1.86368526853621,48.6995010352693;;car;fastest;polyline;true;true diff --git a/test/load/gatling/user-files/resources/routeOsrm.ssv b/test/load/gatling/user-files/resources/routeOsrm.ssv new file mode 100644 index 0000000..d6b9be0 --- /dev/null +++ b/test/load/gatling/user-files/resources/routeOsrm.ssv @@ -0,0 +1,101 @@ +resource;start;end;intermediates;profile;optimization;geometryFormat;getSteps;getBbox +bduni-idf-osrm;2.44302401952445,48.4137148207752;2.7731701053679,48.868175015063;;;fastest;polyline;true;true +bduni-idf-osrm;2.7731701053679,48.868175015063;2.56277133412659,48.6960634629475;;;fastest;polyline;true;true +bduni-idf-osrm;2.35450194738805,49.0751770148287;1.85215365886688,48.4394219123526;;car;;polyline;true;true +bduni-idf-osrm;2.56277133412659,48.6960634629475;1.74224134907126,48.9479846086819;;car;;polyline;true;true +bduni-idf-osrm;2.23373788073659,48.6410320689902;2.62949723191559,48.6433561834972;;car;;polyline;true;true +bduni-idf-osrm;1.85215365886688,48.4394219123526;1.77042682394385,48.6283194552176;;car;;polyline;true;true +bduni-idf-osrm;2.63650365769863,49.0903410648694;3.14853146634996,48.9014406567905;;;;polyline;true;true +bduni-idf-osrm;1.74224134907126,48.9479846086819;2.08896000832319,49.0502271274105;;car;fastest;polyline;true;true +bduni-idf-osrm;1.97092077694833,48.887046989752;2.41960498057306,49.0230359021341;;car;fastest;polyline;true;true +bduni-idf-osrm;2.62949723191559,48.6433561834972;3.03651972003281,48.6488741465611;;;;polyline;true;true +bduni-idf-osrm;2.33698104396462,48.5964256068459;1.72928794324398,48.8457934149308;;;fastest;polyline;true;true +bduni-idf-osrm;1.77042682394385,48.6283194552176;2.29819886684418,48.839445828693;;;fastest;polyline;true;true +bduni-idf-osrm;2.15232158452272,48.7187696998008;1.94806619845331,48.4511461691232;;;;polyline;true;true +bduni-idf-osrm;3.14853146634996,48.9014406567905;3.05042917504907,48.5749592368724;;car;;polyline;true;true +bduni-idf-osrm;3.17251162007451,48.6573443401372;2.33938915580511,49.036685355613;;;;polyline;true;true +bduni-idf-osrm;2.08896000832319,49.0502271274105;2.82994266860187,49.0329269678332;;car;;polyline;true;true +bduni-idf-osrm;2.68978281058371,48.8112607997376;3.02433839887381,49.0986915396061;;car;;polyline;true;true +bduni-idf-osrm;2.41960498057306,49.0230359021341;2.61582909338176,48.7528171760961;;car;fastest;polyline;true;true +bduni-idf-osrm;3.07776795178652,49.0640629309462;3.29302724190056,48.4269115800736;;;;polyline;true;true +bduni-idf-osrm;3.03651972003281,48.6488741465611;2.9313952114433,49.0990003583254;;;;polyline;true;true +bduni-idf-osrm;1.71925774142146,48.551671803114;1.9602685533464,49.0086243897676;;car;;polyline;true;true +bduni-idf-osrm;1.72928794324398,48.8457934149308;1.87770117819309,48.959950564173;;car;fastest;polyline;true;true +bduni-idf-osrm;2.28236234672368,48.561669227411;2.80005635507405,48.6121139103547;;;;polyline;true;true +bduni-idf-osrm;2.29819886684418,48.839445828693;1.80565406046808,48.8567842453951;;;;polyline;true;true +bduni-idf-osrm;2.65340593419969,48.5115093970904;2.22147331535816,48.7309725905536;;car;;polyline;true;true +bduni-idf-osrm;1.94806619845331,48.4511461691232;2.21968059018254,48.9512196605792;;;;polyline;true;true +bduni-idf-osrm;1.79766307622194,49.0639751890674;2.04447524771094,48.604622562998;;;fastest;polyline;true;true +bduni-idf-osrm;3.05042917504907,48.5749592368724;2.7201017331332,49.03883580931;;car;fastest;polyline;true;true +bduni-idf-osrm;2.94450366199017,49.0480559564894;2.92997411340475,48.7645577440504;;car;fastest;polyline;true;true +bduni-idf-osrm;2.33938915580511,49.036685355613;3.13090001307428,49.0978166022105;;car;;polyline;true;true +bduni-idf-osrm;3.00574745200574,48.6070149338106;2.14300098456442,48.4424930648878;;;fastest;polyline;true;true +bduni-idf-osrm;2.82994266860187,49.0329269678332;3.03060300946236,48.9548852890963;;;fastest;polyline;true;true +bduni-idf-osrm;1.75955604799092,48.7252603389323;3.02805497646332,48.7392871859018;;;fastest;polyline;true;true +bduni-idf-osrm;3.02433839887381,49.0986915396061;3.2080610755831,48.9742621842306;;car;fastest;polyline;true;true +bduni-idf-osrm;1.86801247894764,48.5516593190609;1.89905630238354,48.9370307860896;;;fastest;polyline;true;true +bduni-idf-osrm;2.61582909338176,48.7528171760961;2.74377198666334,48.5289590148954;;;;polyline;true;true +bduni-idf-osrm;3.2478683128953,49.0825153477024;2.52931407950819,48.82735147723;;car;;polyline;true;true +bduni-idf-osrm;3.29302724190056,48.4269115800736;2.52194954827428,48.7472926355898;;;;polyline;true;true +bduni-idf-osrm;1.74633691161871,49.0965433740057;2.00636674277484,48.590260257991;;;fastest;polyline;true;true +bduni-idf-osrm;2.9313952114433,49.0990003583254;2.77043087631464,48.698779434571;;;fastest;polyline;true;true +bduni-idf-osrm;3.10948592796922,49.0504662042484;2.08577245622873,48.5230459463317;;;;polyline;true;true +bduni-idf-osrm;1.9602685533464,49.0086243897676;3.00240580327809,49.011906299321;;car;fastest;polyline;true;true +bduni-idf-osrm;2.28719979822636,48.7777114449535;2.51701880581677,48.4398246585857;;car;;polyline;true;true +bduni-idf-osrm;1.87770117819309,48.959950564173;3.13924142792821,48.7964165377198;;;fastest;polyline;true;true +bduni-idf-osrm;2.85333677828312,48.6062430019956;1.85347410105169,48.5623292586068;;;;polyline;true;true +bduni-idf-osrm;2.80005635507405,48.6121139103547;1.71951768808067,48.4922047724947;;car;;polyline;true;true +bduni-idf-osrm;2.86846008636057,48.8194918020628;2.65721126645803,49.0341246748343;;car;fastest;polyline;true;true +bduni-idf-osrm;1.80565406046808,48.8567842453951;3.15767398253083,48.4921172051691;;car;;polyline;true;true +bduni-idf-osrm;2.21233152970672,49.0853195460746;1.74986352920532,48.4846012684749;;car;fastest;polyline;true;true +bduni-idf-osrm;2.22147331535816,48.7309725905536;3.24412962421775,48.5043474580627;;;;polyline;true;true +bduni-idf-osrm;2.46903090253472,48.7825470240787;2.24155541658401,48.924730171659;;car;;polyline;true;true +bduni-idf-osrm;2.21968059018254,48.9512196605792;2.07961494810879,48.4085965128383;;;;polyline;true;true +bduni-idf-osrm;2.15995734296739,48.6497260325123;3.08779237866402,48.9104905051412;;;;polyline;true;true +bduni-idf-osrm;2.04447524771094,48.604622562998;2.26028655618429,48.65906533706;;car;;polyline;true;true +bduni-idf-osrm;2.63795228265226,48.470772205526;2.10360035635531,48.7667713400209;;car;;polyline;true;true +bduni-idf-osrm;2.7201017331332,49.03883580931;2.92441692948341,49.0818783620838;;car;;polyline;true;true +bduni-idf-osrm;3.24241158999503,48.5104270996992;2.68016149103642,48.9174412503606;;;fastest;polyline;true;true +bduni-idf-osrm;2.92997411340475,48.7645577440504;2.25455458052456,48.5617117720889;;;fastest;polyline;true;true +bduni-idf-osrm;3.10586747080088,48.8151862436673;2.11147376447916,48.7175056818407;;;;polyline;true;true +bduni-idf-osrm;3.13090001307428,49.0978166022105;3.06813656724989,49.0666487353155;;;;polyline;true;true +bduni-idf-osrm;1.75176373198628,48.6717312326888;2.97977443821728,48.5418979424285;;car;;polyline;true;true +bduni-idf-osrm;2.14300098456442,48.4424930648878;1.77868135385215,48.6940694178222;;;fastest;polyline;true;true +bduni-idf-osrm;1.77052700333297,48.5874861966819;2.28014034964144,48.4508814013563;;;fastest;polyline;true;true +bduni-idf-osrm;3.03060300946236,48.9548852890963;2.16519124992192,48.4554131807759;;car;fastest;polyline;true;true +bduni-idf-osrm;1.82130594663322,48.9569179091603;2.56359329149127,49.0558625487611;;car;fastest;polyline;true;true +bduni-idf-osrm;3.02805497646332,48.7392871859018;2.20090577974915,48.8656499942997;;;;polyline;true;true +bduni-idf-osrm;1.77758952975273,48.8166411499027;1.81617962121964,49.029776561656;;;;polyline;true;true +bduni-idf-osrm;3.2080610755831,48.9742621842306;2.3203807298094,49.0524362850701;;car;;polyline;true;true +bduni-idf-osrm;1.79864390790462,48.8007683404721;2.40034448355436,49.0499207292916;;;fastest;polyline;true;true +bduni-idf-osrm;1.89905630238354,48.9370307860896;3.01475152149796,48.7522216537734;;;fastest;polyline;true;true +bduni-idf-osrm;2.80626571998,48.7127389817731;2.9590084515512,48.7590084518772;;;fastest;polyline;true;true +bduni-idf-osrm;2.74377198666334,48.5289590148954;2.66359091699123,48.5864080399973;;car;fastest;polyline;true;true +bduni-idf-osrm;2.62659822516143,49.0045754485996;2.69223699234426,48.781094395183;;car;fastest;polyline;true;true +bduni-idf-osrm;2.52931407950819,48.82735147723;1.84350316822529,48.7271079975413;;car;fastest;polyline;true;true +bduni-idf-osrm;2.92537104487419,48.4460248626536;2.06936389505863,48.4106162068434;;car;;polyline;true;true +bduni-idf-osrm;2.52194954827428,48.7472926355898;2.12147863954306,48.6167812872445;;car;fastest;polyline;true;true +bduni-idf-osrm;3.00405092947185,48.928900136007;3.05554540157318,48.9368265053024;;car;fastest;polyline;true;true +bduni-idf-osrm;2.00636674277484,48.590260257991;1.72362964600325,48.8008628757438;;;;polyline;true;true +bduni-idf-osrm;2.44998060241342,48.9409831087571;2.58977488055825,48.5484650451224;;;fastest;polyline;true;true +bduni-idf-osrm;2.77043087631464,48.698779434571;2.85722216144204,48.8624128093012;;;;polyline;true;true +bduni-idf-osrm;2.36744628064334,48.5593186914921;2.24830394499004,48.447400964587;;car;;polyline;true;true +bduni-idf-osrm;2.08577245622873,48.5230459463317;1.89837341569364,48.5615852880524;;;fastest;polyline;true;true +bduni-idf-osrm;2.97855033129454,48.4272446077317;1.86097037009895,48.9765269104158;;;fastest;polyline;true;true +bduni-idf-osrm;3.00240580327809,49.011906299321;1.77136054523289,48.5881791703403;;;;polyline;true;true +bduni-idf-osrm;2.01686525307596,48.559019746189;2.92506045550108,48.8759416235844;;car;fastest;polyline;true;true +bduni-idf-osrm;2.51701880581677,48.4398246585857;1.82099920958281,48.9100377754308;;;;polyline;true;true +bduni-idf-osrm;2.54839190617204,48.508401370002;2.26064089313149,48.600889604562;;;;polyline;true;true +bduni-idf-osrm;3.13924142792821,48.7964165377198;2.33598023504019,48.9742363027763;;car;fastest;polyline;true;true +bduni-idf-osrm;3.1219716783613,48.9284332596697;1.93459122255445,48.9302670808276;;;;polyline;true;true +bduni-idf-osrm;1.85347410105169,48.5623292586068;1.87565202154219,48.8267301582033;;car;fastest;polyline;true;true +bduni-idf-osrm;2.2769722353667,49.0718624827452;2.48952550180256,48.8390767888166;;;fastest;polyline;true;true +bduni-idf-osrm;1.71951768808067,48.4922047724947;2.371133909747,48.6451014467282;;;;polyline;true;true +bduni-idf-osrm;2.27931108362973,48.5834542725701;1.78422989808023,48.6993033653358;;;;polyline;true;true +bduni-idf-osrm;2.65721126645803,49.0341246748343;2.4685234580189,48.4883759838995;;;fastest;polyline;true;true +bduni-idf-osrm;1.72670273520052,48.6091325195273;3.18409432508051,48.6388877378544;;car;fastest;polyline;true;true +bduni-idf-osrm;3.15767398253083,48.4921172051691;2.56231252923608,48.6639785184991;;;fastest;polyline;true;true +bduni-idf-osrm;2.33809377290308,49.0662992510945;2.64215860851109,48.984148150729;;car;;polyline;true;true +bduni-idf-osrm;1.74986352920532,48.4846012684749;3.15085980184376,48.600839496241;;;fastest;polyline;true;true +bduni-idf-osrm;1.88929816000164,48.7471217040671;1.97713369987905,48.791406722297;;car;;polyline;true;true +bduni-idf-osrm;3.24412962421775,48.5043474580627;1.82923176512122,48.8328967497917;;car;;polyline;true;true diff --git a/test/load/gatling/user-files/resources/routePgr.ssv b/test/load/gatling/user-files/resources/routePgr.ssv new file mode 100644 index 0000000..35d9913 --- /dev/null +++ b/test/load/gatling/user-files/resources/routePgr.ssv @@ -0,0 +1,101 @@ +resource;start;end;intermediates;profile;optimization;geometryFormat;getSteps;getBbox +bduni-idf-pgr;2.44302401952445,48.4137148207752;2.7731701053679,48.868175015063;;;fastest;polyline;true;true +bduni-idf-pgr;2.7731701053679,48.868175015063;2.56277133412659,48.6960634629475;;;fastest;polyline;true;true +bduni-idf-pgr;2.35450194738805,49.0751770148287;1.85215365886688,48.4394219123526;;car;;polyline;true;true +bduni-idf-pgr;2.56277133412659,48.6960634629475;1.74224134907126,48.9479846086819;;car;;polyline;true;true +bduni-idf-pgr;2.23373788073659,48.6410320689902;2.62949723191559,48.6433561834972;;car;;polyline;true;true +bduni-idf-pgr;1.85215365886688,48.4394219123526;1.77042682394385,48.6283194552176;;car;;polyline;true;true +bduni-idf-pgr;2.63650365769863,49.0903410648694;3.14853146634996,48.9014406567905;;;;polyline;true;true +bduni-idf-pgr;1.74224134907126,48.9479846086819;2.08896000832319,49.0502271274105;;car;fastest;polyline;true;true +bduni-idf-pgr;1.97092077694833,48.887046989752;2.41960498057306,49.0230359021341;;car;fastest;polyline;true;true +bduni-idf-pgr;2.62949723191559,48.6433561834972;3.03651972003281,48.6488741465611;;;;polyline;true;true +bduni-idf-pgr;2.33698104396462,48.5964256068459;1.72928794324398,48.8457934149308;;;fastest;polyline;true;true +bduni-idf-pgr;1.77042682394385,48.6283194552176;2.29819886684418,48.839445828693;;;fastest;polyline;true;true +bduni-idf-pgr;2.15232158452272,48.7187696998008;1.94806619845331,48.4511461691232;;;;polyline;true;true +bduni-idf-pgr;3.14853146634996,48.9014406567905;3.05042917504907,48.5749592368724;;car;;polyline;true;true +bduni-idf-pgr;3.17251162007451,48.6573443401372;2.33938915580511,49.036685355613;;;;polyline;true;true +bduni-idf-pgr;2.08896000832319,49.0502271274105;2.82994266860187,49.0329269678332;;car;;polyline;true;true +bduni-idf-pgr;2.68978281058371,48.8112607997376;3.02433839887381,49.0986915396061;;car;;polyline;true;true +bduni-idf-pgr;2.41960498057306,49.0230359021341;2.61582909338176,48.7528171760961;;car;fastest;polyline;true;true +bduni-idf-pgr;3.07776795178652,49.0640629309462;3.29302724190056,48.4269115800736;;;;polyline;true;true +bduni-idf-pgr;3.03651972003281,48.6488741465611;2.9313952114433,49.0990003583254;;;;polyline;true;true +bduni-idf-pgr;1.71925774142146,48.551671803114;1.9602685533464,49.0086243897676;;car;;polyline;true;true +bduni-idf-pgr;1.72928794324398,48.8457934149308;1.87770117819309,48.959950564173;;car;fastest;polyline;true;true +bduni-idf-pgr;2.28236234672368,48.561669227411;2.80005635507405,48.6121139103547;;;;polyline;true;true +bduni-idf-pgr;2.29819886684418,48.839445828693;1.80565406046808,48.8567842453951;;;;polyline;true;true +bduni-idf-pgr;2.65340593419969,48.5115093970904;2.22147331535816,48.7309725905536;;car;;polyline;true;true +bduni-idf-pgr;1.94806619845331,48.4511461691232;2.21968059018254,48.9512196605792;;;;polyline;true;true +bduni-idf-pgr;1.79766307622194,49.0639751890674;2.04447524771094,48.604622562998;;;fastest;polyline;true;true +bduni-idf-pgr;3.05042917504907,48.5749592368724;2.7201017331332,49.03883580931;;car;fastest;polyline;true;true +bduni-idf-pgr;2.94450366199017,49.0480559564894;2.92997411340475,48.7645577440504;;car;fastest;polyline;true;true +bduni-idf-pgr;2.33938915580511,49.036685355613;3.13090001307428,49.0978166022105;;car;;polyline;true;true +bduni-idf-pgr;3.00574745200574,48.6070149338106;2.14300098456442,48.4424930648878;;;fastest;polyline;true;true +bduni-idf-pgr;2.82994266860187,49.0329269678332;3.03060300946236,48.9548852890963;;;fastest;polyline;true;true +bduni-idf-pgr;1.75955604799092,48.7252603389323;3.02805497646332,48.7392871859018;;;fastest;polyline;true;true +bduni-idf-pgr;3.02433839887381,49.0986915396061;3.2080610755831,48.9742621842306;;car;fastest;polyline;true;true +bduni-idf-pgr;1.86801247894764,48.5516593190609;1.89905630238354,48.9370307860896;;;fastest;polyline;true;true +bduni-idf-pgr;2.61582909338176,48.7528171760961;2.74377198666334,48.5289590148954;;;;polyline;true;true +bduni-idf-pgr;3.2478683128953,49.0825153477024;2.52931407950819,48.82735147723;;car;;polyline;true;true +bduni-idf-pgr;3.29302724190056,48.4269115800736;2.52194954827428,48.7472926355898;;;;polyline;true;true +bduni-idf-pgr;1.74633691161871,49.0965433740057;2.00636674277484,48.590260257991;;;fastest;polyline;true;true +bduni-idf-pgr;2.9313952114433,49.0990003583254;2.77043087631464,48.698779434571;;;fastest;polyline;true;true +bduni-idf-pgr;3.10948592796922,49.0504662042484;2.08577245622873,48.5230459463317;;;;polyline;true;true +bduni-idf-pgr;1.9602685533464,49.0086243897676;3.00240580327809,49.011906299321;;car;fastest;polyline;true;true +bduni-idf-pgr;2.28719979822636,48.7777114449535;2.51701880581677,48.4398246585857;;car;;polyline;true;true +bduni-idf-pgr;1.87770117819309,48.959950564173;3.13924142792821,48.7964165377198;;;fastest;polyline;true;true +bduni-idf-pgr;2.85333677828312,48.6062430019956;1.85347410105169,48.5623292586068;;;;polyline;true;true +bduni-idf-pgr;2.80005635507405,48.6121139103547;1.71951768808067,48.4922047724947;;car;;polyline;true;true +bduni-idf-pgr;2.86846008636057,48.8194918020628;2.65721126645803,49.0341246748343;;car;fastest;polyline;true;true +bduni-idf-pgr;1.80565406046808,48.8567842453951;3.15767398253083,48.4921172051691;;car;;polyline;true;true +bduni-idf-pgr;2.21233152970672,49.0853195460746;1.74986352920532,48.4846012684749;;car;fastest;polyline;true;true +bduni-idf-pgr;2.22147331535816,48.7309725905536;3.24412962421775,48.5043474580627;;;;polyline;true;true +bduni-idf-pgr;2.46903090253472,48.7825470240787;2.24155541658401,48.924730171659;;car;;polyline;true;true +bduni-idf-pgr;2.21968059018254,48.9512196605792;2.07961494810879,48.4085965128383;;;;polyline;true;true +bduni-idf-pgr;2.15995734296739,48.6497260325123;3.08779237866402,48.9104905051412;;;;polyline;true;true +bduni-idf-pgr;2.04447524771094,48.604622562998;2.26028655618429,48.65906533706;;car;;polyline;true;true +bduni-idf-pgr;2.63795228265226,48.470772205526;2.10360035635531,48.7667713400209;;car;;polyline;true;true +bduni-idf-pgr;2.7201017331332,49.03883580931;2.92441692948341,49.0818783620838;;car;;polyline;true;true +bduni-idf-pgr;3.24241158999503,48.5104270996992;2.68016149103642,48.9174412503606;;;fastest;polyline;true;true +bduni-idf-pgr;2.92997411340475,48.7645577440504;2.25455458052456,48.5617117720889;;;fastest;polyline;true;true +bduni-idf-pgr;3.10586747080088,48.8151862436673;2.11147376447916,48.7175056818407;;;;polyline;true;true +bduni-idf-pgr;3.13090001307428,49.0978166022105;3.06813656724989,49.0666487353155;;;;polyline;true;true +bduni-idf-pgr;1.75176373198628,48.6717312326888;2.97977443821728,48.5418979424285;;car;;polyline;true;true +bduni-idf-pgr;2.14300098456442,48.4424930648878;1.77868135385215,48.6940694178222;;;fastest;polyline;true;true +bduni-idf-pgr;1.77052700333297,48.5874861966819;2.28014034964144,48.4508814013563;;;fastest;polyline;true;true +bduni-idf-pgr;3.03060300946236,48.9548852890963;2.16519124992192,48.4554131807759;;car;fastest;polyline;true;true +bduni-idf-pgr;1.82130594663322,48.9569179091603;2.56359329149127,49.0558625487611;;car;fastest;polyline;true;true +bduni-idf-pgr;3.02805497646332,48.7392871859018;2.20090577974915,48.8656499942997;;;;polyline;true;true +bduni-idf-pgr;1.77758952975273,48.8166411499027;1.81617962121964,49.029776561656;;;;polyline;true;true +bduni-idf-pgr;3.2080610755831,48.9742621842306;2.3203807298094,49.0524362850701;;car;;polyline;true;true +bduni-idf-pgr;1.79864390790462,48.8007683404721;2.40034448355436,49.0499207292916;;;fastest;polyline;true;true +bduni-idf-pgr;1.89905630238354,48.9370307860896;3.01475152149796,48.7522216537734;;;fastest;polyline;true;true +bduni-idf-pgr;2.80626571998,48.7127389817731;2.9590084515512,48.7590084518772;;;fastest;polyline;true;true +bduni-idf-pgr;2.74377198666334,48.5289590148954;2.66359091699123,48.5864080399973;;car;fastest;polyline;true;true +bduni-idf-pgr;2.62659822516143,49.0045754485996;2.69223699234426,48.781094395183;;car;fastest;polyline;true;true +bduni-idf-pgr;2.52931407950819,48.82735147723;1.84350316822529,48.7271079975413;;car;fastest;polyline;true;true +bduni-idf-pgr;2.92537104487419,48.4460248626536;2.06936389505863,48.4106162068434;;car;;polyline;true;true +bduni-idf-pgr;2.52194954827428,48.7472926355898;2.12147863954306,48.6167812872445;;car;fastest;polyline;true;true +bduni-idf-pgr;3.00405092947185,48.928900136007;3.05554540157318,48.9368265053024;;car;fastest;polyline;true;true +bduni-idf-pgr;2.00636674277484,48.590260257991;1.72362964600325,48.8008628757438;;;;polyline;true;true +bduni-idf-pgr;2.44998060241342,48.9409831087571;2.58977488055825,48.5484650451224;;;fastest;polyline;true;true +bduni-idf-pgr;2.77043087631464,48.698779434571;2.85722216144204,48.8624128093012;;;;polyline;true;true +bduni-idf-pgr;2.36744628064334,48.5593186914921;2.24830394499004,48.447400964587;;car;;polyline;true;true +bduni-idf-pgr;2.08577245622873,48.5230459463317;1.89837341569364,48.5615852880524;;;fastest;polyline;true;true +bduni-idf-pgr;2.97855033129454,48.4272446077317;1.86097037009895,48.9765269104158;;;fastest;polyline;true;true +bduni-idf-pgr;3.00240580327809,49.011906299321;1.77136054523289,48.5881791703403;;;;polyline;true;true +bduni-idf-pgr;2.01686525307596,48.559019746189;2.92506045550108,48.8759416235844;;car;fastest;polyline;true;true +bduni-idf-pgr;2.51701880581677,48.4398246585857;1.82099920958281,48.9100377754308;;;;polyline;true;true +bduni-idf-pgr;2.54839190617204,48.508401370002;2.26064089313149,48.600889604562;;;;polyline;true;true +bduni-idf-pgr;3.13924142792821,48.7964165377198;2.33598023504019,48.9742363027763;;car;fastest;polyline;true;true +bduni-idf-pgr;3.1219716783613,48.9284332596697;1.93459122255445,48.9302670808276;;;;polyline;true;true +bduni-idf-pgr;1.85347410105169,48.5623292586068;1.87565202154219,48.8267301582033;;car;fastest;polyline;true;true +bduni-idf-pgr;2.2769722353667,49.0718624827452;2.48952550180256,48.8390767888166;;;fastest;polyline;true;true +bduni-idf-pgr;1.71951768808067,48.4922047724947;2.371133909747,48.6451014467282;;;;polyline;true;true +bduni-idf-pgr;2.27931108362973,48.5834542725701;1.78422989808023,48.6993033653358;;;;polyline;true;true +bduni-idf-pgr;2.65721126645803,49.0341246748343;2.4685234580189,48.4883759838995;;;fastest;polyline;true;true +bduni-idf-pgr;1.72670273520052,48.6091325195273;3.18409432508051,48.6388877378544;;car;fastest;polyline;true;true +bduni-idf-pgr;3.15767398253083,48.4921172051691;2.56231252923608,48.6639785184991;;;fastest;polyline;true;true +bduni-idf-pgr;2.33809377290308,49.0662992510945;2.64215860851109,48.984148150729;;car;;polyline;true;true +bduni-idf-pgr;1.74986352920532,48.4846012684749;3.15085980184376,48.600839496241;;;fastest;polyline;true;true +bduni-idf-pgr;1.88929816000164,48.7471217040671;1.97713369987905,48.791406722297;;car;;polyline;true;true +bduni-idf-pgr;3.24412962421775,48.5043474580627;1.82923176512122,48.8328967497917;;car;;polyline;true;true diff --git a/test/load/gatling/user-files/simulations/road2_route.scala b/test/load/gatling/user-files/simulations/dataOsm.scala similarity index 74% rename from test/load/gatling/user-files/simulations/road2_route.scala rename to test/load/gatling/user-files/simulations/dataOsm.scala index 1a00690..d2a731c 100644 --- a/test/load/gatling/user-files/simulations/road2_route.scala +++ b/test/load/gatling/user-files/simulations/dataOsm.scala @@ -2,9 +2,9 @@ import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ -class road2RouteLoadTest extends Simulation { +class dataOsm extends Simulation { - val urls = ssv("./resources/road2_parameters.ssv").shuffle.circular + val urls = ssv("/home/docker/data/road2_parameters.ssv").shuffle.circular val httpConf = http.baseUrl("http://road2:8080/simple/1.0.0/route?").disableCaching @@ -17,8 +17,6 @@ class road2RouteLoadTest extends Simulation { setUp( scn.inject( - // nothingFor(5 seconds), - // rampUsersPerSec (0) to (10) during (60 seconds), constantUsersPerSec(1) during (100 seconds) randomized ).protocols(httpConf)) diff --git a/test/load/gatling/user-files/simulations/road2_iso.scala b/test/load/gatling/user-files/simulations/isoPgr.scala similarity index 73% rename from test/load/gatling/user-files/simulations/road2_iso.scala rename to test/load/gatling/user-files/simulations/isoPgr.scala index 9755e2d..a4fad8a 100644 --- a/test/load/gatling/user-files/simulations/road2_iso.scala +++ b/test/load/gatling/user-files/simulations/isoPgr.scala @@ -2,9 +2,9 @@ import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ -class road2IsoLoadTest extends Simulation { +class isoPgr extends Simulation { - val urls = ssv("./resources/road2_parameters_iso.ssv").shuffle.circular + val urls = ssv("./resources/isoPgr.ssv").shuffle.circular val httpConf = http.baseUrl("http://road2:8080/simple/1.0.0/isochrone?").disableCaching @@ -17,8 +17,6 @@ class road2IsoLoadTest extends Simulation { setUp( scn.inject( - // nothingFor(5 seconds), - // rampUsersPerSec (0) to (10) during (60 seconds), constantUsersPerSec(1) during (60 seconds) randomized ).protocols(httpConf)) diff --git a/test/load/gatling/user-files/simulations/road2_route_pgr.scala b/test/load/gatling/user-files/simulations/routeOsrm.scala similarity index 65% rename from test/load/gatling/user-files/simulations/road2_route_pgr.scala rename to test/load/gatling/user-files/simulations/routeOsrm.scala index 044b3a5..532ecfb 100644 --- a/test/load/gatling/user-files/simulations/road2_route_pgr.scala +++ b/test/load/gatling/user-files/simulations/routeOsrm.scala @@ -2,9 +2,9 @@ import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ -class road2RouteLoadTestPgr extends Simulation { +class routeOsrm extends Simulation { - val urls = ssv("./resources/road2_parameters_idf_pgr_wo_int.ssv").shuffle.circular + val urls = ssv("./resources/routeOsrm.ssv").shuffle.circular val httpConf = http.baseUrl("http://road2:8080/simple/1.0.0/route?").disableCaching @@ -17,9 +17,7 @@ class road2RouteLoadTestPgr extends Simulation { setUp( scn.inject( - // nothingFor(5 seconds), - // rampUsersPerSec (0) to (10) during (60 seconds), - constantUsersPerSec(1) during (300 seconds) randomized + constantUsersPerSec(1) during (60 seconds) randomized ).protocols(httpConf)) } diff --git a/test/load/gatling/user-files/simulations/routePgr.scala b/test/load/gatling/user-files/simulations/routePgr.scala new file mode 100644 index 0000000..b716743 --- /dev/null +++ b/test/load/gatling/user-files/simulations/routePgr.scala @@ -0,0 +1,23 @@ +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ +import scala.concurrent.duration._ + +class routePgr extends Simulation { + + val urls = ssv("./resources/routePgr.ssv").shuffle.circular + + val httpConf = http.baseUrl("http://road2:8080/simple/1.0.0/route?").disableCaching + + val scn = scenario("road2") + .feed(urls).repeat(1){ + exec( + http("compute").get("resource=${resource}&profile=${profile}&optimization=${optimization}&start=${start}&end=${end}&intermediates=${intermediates}&geometryFormat=${geometryFormat}&getSteps=${getSteps}&getBbox=${getBbox}") + ) + } + + setUp( + scn.inject( + constantUsersPerSec(1) during (60 seconds) randomized + ).protocols(httpConf)) + +} diff --git a/test/load/readme.md b/test/load/readme.md index c1771fc..f4ab26d 100644 --- a/test/load/readme.md +++ b/test/load/readme.md @@ -6,12 +6,11 @@ Ce dossier contient les scripts utiles aux tests de charges. Ces tests sont effe Le dossier `gatling` contient le dossier `user-files` nécessaire à Getling pour effectuer les tests. On y retrouve donc la définition des simulations et les ressources nécessaire. En l'état, il est possible de lancer le scénario contenu dans `gatling/user-files/simulations/road2.scala` qui utilise la ressource `gatling/user-files/resources/road2_parameters.ssv`. -Si Gatling est installé sur la machine, on pourra pointer le dossier `user-files`. De la même manière, il est possible d'utiliser Docker. +Si Gatling est installé sur la machine, on pourra pointer le dossier `user-files`. Pour plus d'informations, voir le site [officiel](https://gatling.io/). -Avec le `docker-compose` du repository, il est possible de lancer les tests de charge avec les données déjà disponibles: -`docker-compose up road2-gatling` +Autrement, il est possible d'utiliser l'image docker disponible sur [dockerhub](https://hub.docker.com/r/denvazh/gatling). -Pour utiliser le `docker-compose` avec d'autres données, il suffira de modifier le `.env` associé et ainsi de pointer vers un autre `user-files`. On priviligiera cette approche à celle modifiant les fichiers de ce repository. +C'est ce qui est fait dans le [docker-compose](../../docker/test/) dédié aux tests dans ce dépôt. Voir le [readme](../../docker/test/readme.md) pour son utilisation. ## random-route-generator From ae5cc479af272a57965524625741c2f406d73455 Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 1 Dec 2022 14:36:07 +0100 Subject: [PATCH 22/93] =?UTF-8?q?[deps]=20maj=20de=20nombreuses=20d=C3=A9p?= =?UTF-8?q?endances?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 12 +++ docker/demonstration/Dockerfile | 2 +- package.json | 18 ++-- readme.md | 2 +- .../cucumber/features/support/world.js | 2 +- test/integration/readme.md | 101 +++++++++++++++++- 6 files changed, 124 insertions(+), 13 deletions(-) diff --git a/changelog.md b/changelog.md index 42c3f18..bf11cea 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,18 @@ CHANGED: - Les sources ne sont plus configurées dans le même fichier que les ressources. Chaque source est configurée dans son fichier. L'ensemble est placé dans un dossier de sources. Il peut y en avoir plusieurs. - Les sources PGRouting et Valhalla ne sont plus configurées de la même manière : chaque source de ces types peut contenir plusieurs coûts. +UPDATED: + - Passage à `osrm` 5.26.0 + - Passage à `pg` 8.8.0 + - Passage à `turf` 6.5.0 + - Passage à `express` 4.18.2 + - Passage à `helmet` 6.0.1 + - Passage à `https-proxy-agent` 5.0.1 + - Passage à `log4js` 6.7.1 + - Passage à `nconf` 0.12.0 + - Passage à `proj4` 2.8.0 + - Utilisation de NodeJS 16 dans docker + # 1.1.2 FIXED: diff --git a/docker/demonstration/Dockerfile b/docker/demonstration/Dockerfile index 62e9ea0..301bb70 100644 --- a/docker/demonstration/Dockerfile +++ b/docker/demonstration/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12-alpine +FROM node:16-alpine ### Dossier contenant la configuration WORKDIR /home/docker/config diff --git a/package.json b/package.json index 83c4c1b..2582d2c 100644 --- a/package.json +++ b/package.json @@ -18,21 +18,21 @@ }, "dependencies": { "@mapbox/polyline": "1.1.1", - "@turf/turf": "5.1.6", + "@turf/turf": "6.5.0", "assert": "2.0.0", "cors": "2.8.5", - "express": "4.16.4", + "express": "4.18.2", "got": "11.8.2", - "helmet": "3.21.2", - "https-proxy-agent": "5.0.0", - "log4js": "6.1.0", - "nconf": "0.10.0", - "proj4": "2.6.0", + "helmet": "6.0.1", + "https-proxy-agent": "5.0.1", + "log4js": "6.7.1", + "nconf": "0.12.0", + "proj4": "2.8.0", "wkt": "0.1.1" }, "optionalDependencies": { - "osrm": "5.25.0", - "pg": "7.17.1" + "osrm": "5.26.0", + "pg": "8.8.0" }, "devDependencies": { "sinon": "^7.2.7", diff --git a/readme.md b/readme.md index 05ca3ae..ef6fb6a 100644 --- a/readme.md +++ b/readme.md @@ -58,7 +58,7 @@ NB : Il y a des dépendances optionnelles pour gérer celles de chaque moteur. P ### Génération de données Qu'importe la source des données, il est nécessaire de les fournir dans l'un des formats utilisables par Road2. Étant donné que ce dernier peut utiliser plusieurs moteurs les calculs, il accepte plusieurs formats de données: -- OSRM 5.25.0 rend possible l'utilisation de données OSRM générées avec cette version. +- OSRM 5.26.0 rend possible l'utilisation de données OSRM générées avec cette version. - PGRouting 3.1.3 rend possible l'utilisation d'une base de données utilisant cette version. Il sera nécessaire d'y ajouter les procédures du projet [pgrouting-procedures](https://github.com/IGNF/pgrouting-procedures) afin que Road2 puisse communiquer avec la base. Ces données peuvent donc être générées à partir d'une base de données quelconque, ou de fichiers OSM. Le projet [route-graph-generator](https://github.com/IGNF/route-graph-generator) propose des outils pour générer les graphes à partir de n'importe quelle base de données ou fichier osm. Si la base de données ne correspondant pas au format de la base attendue par route-graph-generator, il suffira de la dériver. diff --git a/test/functional/configuration/cucumber/features/support/world.js b/test/functional/configuration/cucumber/features/support/world.js index 50a8c30..9fc42bd 100644 --- a/test/functional/configuration/cucumber/features/support/world.js +++ b/test/functional/configuration/cucumber/features/support/world.js @@ -662,7 +662,7 @@ class road2World { if (this._cleanTmpDirectories) { - fs.rmdir(this._tmpDirConf, { + fs.rm(this._tmpDirConf, { "recursive": true }, (err) => { return err;}); diff --git a/test/integration/readme.md b/test/integration/readme.md index 3d54512..a733d48 100644 --- a/test/integration/readme.md +++ b/test/integration/readme.md @@ -1,5 +1,7 @@ # Description des tests d'intégration +## Tests des classes + Pour lancer les tests d'intégration, il est conseillé d'utiliser docker-compose afin de disposer d'un environnement de test plus complet: ``` docker-compose up -d road2 @@ -64,4 +66,101 @@ Autres: - controller.js de l'api simple 1.0.0 - index.js de l'api simple 1.0.0 - init.js de l'api simple 1.0.0 - - update.js de l'api simple 1.0.0 \ No newline at end of file + - update.js de l'api simple 1.0.0 + +## Tests des dépendances + +### Liste des dépendances et de leurs usages + +- @mapbox/polyline + - geometry/line.js + - encode() + - toGeoJSON() + - fromGeoJSON() + - geometry/polygon.js + - encode() + - toGeoJSON() + - fromGeoJSON() + +- @turf/turf + - apis/simple/1.0.0/controller/controller.js + - bbox() + - lineSlice() + - geometry/polygon.js + - polygon() + - polygonToLine() + - sources/pgrSource.js + - point() + - truncate() + - lineSlice() + - nearestPointOnLine() + - length() + - cleanCoords() + +- assert + - deepStrictEqual() + - equal() + - deepEqual() + +- cors + - service/service.js + - () + +- express + - administrator/administrator.js + - () + - use() + - Router() + - router.use() + - json() + +- got + - utils/httpQuery.js + - () + - post() + +- helmet + - administrator/administrator.js + - () + - service/service.js + - () + +- https-proxy-agent + - utils/httpQuery.js + - () + +- log4js + - configure() + - getLogger() + +- nconf + - road2.js + - use() + - get() + - argv().get() + - argv().env() + +- proj4 + - geography/projectionManager.js + - defs() + - () + +- wkt + - sources/smartroutingSource.js + - parse() + +- osrm + - sources/sourceManager.js + - sources/osrmSource.js + - route() + - nearest() + +- pg {Pool} + - base/base.js + - () + - connect() + - end() + - base/baseManager.js + - sources/sourcesManager.js + - sources/pgrSource.js + - query() \ No newline at end of file From e0ca4582349ccb6774d7f12acbb1d5007eb20767 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 5 Dec 2022 10:29:56 +0100 Subject: [PATCH 23/93] [docker] build de valhalla pour les dev --- docker/demonstration/readme.md | 2 +- docker/dev/docker-compose.yml | 2 +- docker/distributions/centos-wo-aws/Dockerfile | 128 ------------------ docker/distributions/centos-wo-aws/readme.md | 69 ---------- docker/distributions/centos/Dockerfile | 91 ------------- docker/distributions/centos/readme.md | 55 -------- docker/distributions/debian/Dockerfile | 115 ++++++---------- docker/readme.md | 4 +- docker/route-graph-generator | 2 +- documentation/developers/version.md | 8 +- src/js/sources/sourceManager.js | 1 + .../features/req-simple-1.0.0-pgr.feature | 66 +++++++++ 12 files changed, 120 insertions(+), 423 deletions(-) delete mode 100644 docker/distributions/centos-wo-aws/Dockerfile delete mode 100644 docker/distributions/centos-wo-aws/readme.md delete mode 100644 docker/distributions/centos/Dockerfile delete mode 100644 docker/distributions/centos/readme.md diff --git a/docker/demonstration/readme.md b/docker/demonstration/readme.md index 9f3cea3..97b88b9 100644 --- a/docker/demonstration/readme.md +++ b/docker/demonstration/readme.md @@ -1,6 +1,6 @@ # Démonstration locale de Road2 -Ce fichier décrit les instructions à suivre pour avoir une démonstration locale de Road2. +Ce fichier décrit les instructions à suivre pour avoir une démonstration locale de Road2 limitée aux moteurs OSRM et PGRouting. ## Principe diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index 7def3fc..a7869ec 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -4,7 +4,7 @@ services: road2: build: context: ../.. - dockerfile: ./docker/demonstration/Dockerfile + dockerfile: ./docker/distributions/debian/Dockerfile image: road2 container_name: road2-server depends_on: diff --git a/docker/distributions/centos-wo-aws/Dockerfile b/docker/distributions/centos-wo-aws/Dockerfile deleted file mode 100644 index 5fe080f..0000000 --- a/docker/distributions/centos-wo-aws/Dockerfile +++ /dev/null @@ -1,128 +0,0 @@ -FROM centos:7.8.2003 - -LABEL maintainer="IGN " -LABEL version="1.2" - -### MAJ -RUN yum -y update && \ - yum -y upgrade && \ - yum install -y yum-utils centos-release-scl epel-release && \ - yum-config-manager --enable rhel-server-rhscl-7-rpms - -### Utilitaires et dépendances -# Pour NodeJS: python-2.7.5 -# Un compilateur pour Boost et GCC 6: gcc-c++ -# Un compilateur c++ plus récent (8) pour NodeJS: devtoolset-8 -# Pour GCC 6.3.0 afin de compiler OSRM: GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+ -# Pour OSRM: boost 1.65.1; expat 2.2.0; lua 5.2.4; bzip2 1.0.6; tbb -# Pour Boost: openmpi-devel python-devel -# Pour LUA: readline-devel -RUN yum -y install wget vim git python-2.7.5 gcc-c++ devtoolset-8 gmp-devel \ - mpfr-devel libmpc-devel flex flex-devel cmake3 zlib zlib-devel tbb tbb-devel \ - expat expat-devel lbzip2 bzip2-devel openmpi-devel python-devel readline-devel - -### Installation de NodeJS à partir des sources -# https://github.com/nodejs/node/blob/v12.x/BUILDING.md -# https://github.com/nodejs/help/wiki/Installation -WORKDIR /home/docker/nodejs -RUN wget -O node-v12.14.0.tar.gz "https://nodejs.org/dist/v12.14.0/node-v12.14.0.tar.gz" && \ - tar -xzf /home/docker/nodejs/node-v12.14.0.tar.gz && \ - scl enable devtoolset-8 bash && export CXX=/opt/rh/devtoolset-8/root/usr/bin/g++ && \ - cd node-v12.14.0 && ./configure && make -j4 && make install && \ - cd .. && rm -rf node-v12.14.0 node-v12.14.0.tar.gz - -### Installation de gcc 6.2.0 pour compiler OSRM -# La compilation est volontairement en dehors des sources de gcc (voir documentation) -# https://gibsonic.org/tools/2019/08/08/gcc_building.html -WORKDIR /home/docker/gcc -RUN git clone -b releases/gcc-6.3.0 --depth 1 https://gcc.gnu.org/git/gcc.git && \ - mkdir /home/docker/gcc/objdir && cd objdir && \ - /home/docker/gcc/gcc/configure --prefix=$(pwd) --disable-multilib --disable-werror --enable-languages=c,c++ && \ - make -j4 && make DESTDIR=/opt/gcc-6/ install && \ - cd .. && rm -rf gcc objdir - -### Installation des dépendances de OSRM -# https://github.com/Project-OSRM/osrm-backend/wiki/Building-OSRM - -# Installation de boost 1.65.1 (boost, filesystem, iostreams, program-options, regex, test, date-time, thread, system) -WORKDIR /home/docker/boost -RUN wget https://dl.bintray.com/boostorg/release/1.65.1/source/boost_1_65_1.tar.gz && tar -xzf boost_1_65_1.tar.gz && \ - cd boost_1_65_1 && ./bootstrap.sh && ./b2 install && \ - cd .. && rm -rf boost_1_65_1 boost_1_65_1.tar.gz - -# Installation de lua 5.2.4 -WORKDIR /home/docker/lua -RUN wget http://www.lua.org/ftp/lua-5.2.4.tar.gz && tar zxf lua-5.2.4.tar.gz && \ - cd lua-5.2.4 && make linux test && make linux install && \ - cd .. && rm -rf lua-5.2.4 lua-5.2.4.tar.gz - -### Installation de OSRM pour utiliser la libosrm dans Road2 -# https://github.com/Project-OSRM/osrm-backend -# https://github.com/Project-OSRM/osrm-backend/blob/master/docs/nodejs/api.md -# https://github.com/door2door-io/osrm-express-server-demo -# https://github.com/Project-OSRM/osrm-backend/blob/master/CMakeLists.txt -# https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/ -WORKDIR /home/docker/osrm -RUN wget https://github.com/Project-OSRM/osrm-backend/archive/v5.25.0.zip && unzip v5.25.0.zip -d ./osrm-backend && \ - cd /home/docker/osrm/osrm-backend/osrm-backend-5.25.0/ && \ - npm install --production && \ - mkdir build && cd /home/docker/osrm/osrm-backend/osrm-backend-5.25.0/build && \ - export CC=/opt/gcc-6/home/docker/gcc/objdir/bin/gcc && export CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" && \ - cmake3 .. -DCMAKE_BUILD_TYPE=Release -DENABLE_MASON=OFF -DCMAKE_CXX_COMPILER=/opt/gcc-6/home/docker/gcc/objdir/bin/g++ -DENABLE_NODE_BINDINGS=ON && \ - make -j4 && make install -# https://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.how_to_set_paths -ENV LD_LIBRARY_PATH=/opt/gcc-6/home/docker/gcc/objdir/lib64:$LD_LIBRARY_PATH - -### Creation du binding NodeJS d'OSRM -WORKDIR /home/docker/osrm/nodejs-binding/ -# Création du module node osrm et petit nettoyage de ce qui n'est plus utile -RUN mkdir -p osrm-5.25.0/lib && mkdir -p osrm-5.25.0/node_modules && cp -r ../osrm-backend/osrm-backend-5.25.0/lib/* osrm-5.25.0/lib/ && \ - cp /usr/lib64/libtbb* osrm-5.25.0/lib/binding/ && cp -r ../osrm-backend/osrm-backend-5.25.0/node_modules/* osrm-5.25.0/node_modules && \ - cp ../osrm-backend/osrm-backend-5.25.0/package* osrm-5.25.0/ && cp ../osrm-backend/osrm-backend-5.25.0/taginfo.json osrm-5.25.0/ && \ - tar -cvzf osrm-5.25.0.tgz osrm-5.25.0/ && \ - cd .. && rm -rf v5.25.0.zip osrm-backend nodejs-binding/osrm-5.25.0 - -### Dossier des données -WORKDIR /home/docker/internal - -### Récupération de données sur la Corse -RUN wget download.geofabrik.de/europe/france/corse-latest.osm.pbf && osrm-extract corse-latest.osm.pbf -p /usr/local/share/osrm/profiles/car.lua && osrm-contract corse-latest.osrm - -### Dossier contenant la configuration -WORKDIR /home/docker/config -COPY /docker/config /home/docker/config/ - -### Dossier de l'application -WORKDIR /home/docker/app - -### Récupération des sources de l'application -COPY package.json ./ -COPY eslint.json ./ -COPY jsdoc.json ./ -COPY /src ./src/ -COPY /test ./test/ - -### Installation des dépandences de l'application NodeJS -# https://nodejs.org/en/docs/guides/nodejs-docker-webapp/ -# https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md -# https://expressjs.com/fr/starter/installing.html -RUN sed -i 's/"\s*osrm\s*"\s*:\s*".*",/"osrm":"\/home\/docker\/osrm\/nodejs-binding\/osrm-5\.24\.0\.tgz",/g' package.json -RUN npm install - -### Installation de mocha pour les tests -# https://mochajs.org/#installation -RUN npm install mocha -g - -### Installation de eslint pour le code -# https://eslint.org/docs/user-guide/configuring -RUN npm install eslint -g - -### Installation de jsdoc pour la documentation du code -# http://usejsdoc.org/about-configuring-jsdoc.html -RUN npm install jsdoc -g - -### Volume partagé pour lire les données -VOLUME ["/home/docker/data"] - -### Commande de lancement de l'application -CMD npm run debug -- --ROAD2_CONF_FILE=/home/docker/config/road2.json diff --git a/docker/distributions/centos-wo-aws/readme.md b/docker/distributions/centos-wo-aws/readme.md deleted file mode 100644 index 5099c07..0000000 --- a/docker/distributions/centos-wo-aws/readme.md +++ /dev/null @@ -1,69 +0,0 @@ -# Dockerfile pour utiliser Road2 sous CentOS mais sans faire appel à AWS - -Il est possible d'installer OSRM, et plus particulièrement le binding NodeJS, depuis les sources sans faire appel aux binaires hébergés sur AWS. Ce dockerfile en est un exemple. Voici la procèdure à suivre: - -1. Installer un compilateur pour Boost et GCC 6: gcc-c++ -2. Installer les dépendances pour GCC 6.3.0 afin de compiler OSRM: GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+ -3. Installer les dépendances pour OSRM: boost 1.65.1; expat 2.2.0; lua 5.2.4; bzip2 1.0.6; tbb -4. Installer les dépendances pour Boost: openmpi-devel python-devel -5. Installer les dépendances pour LUA: readline-devel -6. Compiler et installer gcc 6.2.0 pour compiler OSRM -7. Compiler et installer boost 1.65.1 -8. Compiler et installer lua 5.2.4 -9. Compiler et installer OSRM 5.25.0 (cf Dockerfile) -10. Ajouter la variable LD_LIBRARY_PATH=/opt/gcc-6/home/docker/gcc/objdir/lib64:$LD_LIBRARY_PATH pour que les binaires trouvent les librairies utilisées lors de la compilation. -11. Créer le module NodeJS d'OSRM (cf. Dockerfile) - - -# Construction de l'image - -Pour construire l'image, il suffit de lancer la commande suivante à la racine du projet Road2: -``` -docker build -t road2-centos -f docker/centos/Dockerfile . -``` - -# Lancer l'application - -Pour lancer l'application, il suffit d'utiliser la commande suivante: -``` -docker run --name road2-centos-server --rm -d -p 8080:8080 road2-centos -``` - -## Mode DEBUG -``` -docker run --name road2-centos-server --rm -it -p 8080:8080 road2-centos /bin/bash -``` - -## Pour développer en gardant le code source en local -``` -docker run --name road2-centos-server --rm -d -p 8080:8080 -v $src:/home/docker/app/src road2-centos -``` - -## Pour débugger le mode développement avec les sources en local -``` -docker run --name road2-centos-server --rm -it -p 8080:8080 -v $src:/home/docker/app/src road2-centos /bin/bash -``` -# Lancer les tests - -Les tests unitaires ont été écrits avec Mocha. Pour les lancer, on utilisera la commande suivante: -``` -docker run --name road2-centos-server --rm -v $src:/home/docker/app/src -v $test:/home/docker/app/test road2-centos npm run utest -``` - -# Lancer eslint - -Pour linter le code, il suffit de lancer la commande suivante: -``` -docker run --name road2-centos-server --rm -v $src:/home/docker/app/src road2-centos npm run lint -``` - -# Créer la documentation du code via jsdoc - -Le code est documenté via des commentaires. Ces commentaires peuvent être plus ou moins structurés avec des tags. L'outil jsdoc permet de générer un site web à partir de ces commentaires et de ces tags. - -Pour créer la documentation, il suffit de lancer la commande suivante: -``` -docker run --name road2-centos-server --rm -v $doc:/home/docker/app/documentation/code road2-centos npm run jsdoc -``` - -La documentation sera alors accessible dans `$doc`. diff --git a/docker/distributions/centos/Dockerfile b/docker/distributions/centos/Dockerfile deleted file mode 100644 index a22ebc7..0000000 --- a/docker/distributions/centos/Dockerfile +++ /dev/null @@ -1,91 +0,0 @@ -FROM centos:7.5.1804 - -LABEL maintainer="IGN " -LABEL version="1.0" - -### MAJ -RUN yum -y update -RUN yum -y upgrade -RUN yum install -y yum-utils centos-release-scl epel-release -RUN yum-config-manager --enable rhel-server-rhscl-7-rpms - -### Utilitaires -RUN yum -y install wget vim - -### Installation des dépendances de NodeJS -RUN yum install -y python-2.7.5 - -### Installation du compilateur c++ -RUN yum -y install devtoolset-8 gcc gcc-c++ - - - -### Installation de NodeJS à partir des sources -# https://github.com/nodejs/node/blob/master/BUILDING.md#building-nodejs-on-supported-platforms -# https://github.com/nodejs/help/wiki/Installation -WORKDIR /home/docker/nodejs -RUN wget -O node-v12.14.0.tar.gz "https://nodejs.org/dist/v12.14.0/node-v12.14.0.tar.gz" -RUN tar -xzf /home/docker/nodejs/node-v12.14.0.tar.gz -RUN cd node-v12.14.0 && ./configure && make -j4 && make install - -### Installation des dépendances de OSRM -# https://github.com/Project-OSRM/osrm-backend/wiki/Building-OSRM -RUN scl enable devtoolset-8 bash && yum -y install cmake3 zlib-devel - -### Installation de OSRM pour utiliser la libosrm dans Road2 -# https://github.com/Project-OSRM/osrm-backend -# https://github.com/Project-OSRM/osrm-backend/blob/master/docs/nodejs/api.md -# https://github.com/door2door-io/osrm-express-server-demo -# https://github.com/Project-OSRM/osrm-backend/blob/master/CMakeLists.txt -WORKDIR /home/docker/osrm -RUN wget https://github.com/Project-OSRM/osrm-backend/archive/v5.25.0.zip -RUN unzip v5.25.0.zip -d ./osrm-backend -WORKDIR /home/docker/osrm/osrm-backend/osrm-backend-5.25.0/build -RUN scl enable devtoolset-8 bash && export CC=/opt/rh/devtoolset-8/root/usr/bin/gcc && cmake3 .. -DCMAKE_BUILD_TYPE=Release -DENABLE_MASON=ON -DCMAKE_CXX_COMPILER=/opt/rh/devtoolset-8/root/usr/bin/g++ && make && make install - -### Dossier des données -WORKDIR /home/docker/internal - -### Récupération de données sur la Corse -# L'objectif est d'avoir un container indépendant du docker-compose -RUN wget download.geofabrik.de/europe/france/corse-latest.osm.pbf -RUN osrm-extract corse-latest.osm.pbf -p /usr/local/share/osrm/profiles/car.lua -RUN osrm-contract corse-latest.osrm - -### Dossier contenant la configuration -WORKDIR /home/docker/config -COPY /docker/config /home/docker/config/ - -### Dossier de l'application -WORKDIR /home/docker/app - -### Récupération des sources de l'application -COPY package.json ./ -COPY eslint.json ./ -COPY jsdoc.json ./ -COPY /src ./src/ -COPY /test ./test/ - -### Installation des dépandences de l'application NodeJS -# https://nodejs.org/en/docs/guides/nodejs-docker-webapp/ -# https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md -# https://expressjs.com/fr/starter/installing.html -RUN npm install - -### Installation de mocha pour les tests -# https://mochajs.org/#installation -RUN npm install mocha -g - -### Installation de eslint pour le code -# https://eslint.org/docs/user-guide/configuring -RUN npm install eslint -g - -### Installation de jsdoc pour la documentation du code -# http://usejsdoc.org/about-configuring-jsdoc.html -RUN npm install jsdoc -g - -### Volume partagé pour lire les données -VOLUME ["/home/docker/data"] - -### Commande de lancement de l'application -CMD npm run debug -- --ROAD2_CONF_FILE=/home/docker/config/road2.json diff --git a/docker/distributions/centos/readme.md b/docker/distributions/centos/readme.md deleted file mode 100644 index 5c4a5f2..0000000 --- a/docker/distributions/centos/readme.md +++ /dev/null @@ -1,55 +0,0 @@ -# Dockerfile pour utiliser Road2 sous CentOS - - -# Construction de l'image - -Pour construire l'image, il suffit de lancer la commande suivante à la racine du projet Road2: -``` -docker build -t road2-centos -f docker/centos/Dockerfile . -``` - -# Lancer l'application - -Pour lancer l'application, il suffit d'utiliser la commande suivante: -``` -docker run --name road2-centos-server --rm -d -p 8080:8080 road2-centos -``` - -## Mode DEBUG -``` -docker run --name road2-centos-server --rm -it -p 8080:8080 road2-centos /bin/bash -``` - -## Pour développer en gardant le code source en local -``` -docker run --name road2-centos-server --rm -d -p 8080:8080 -v $src:/home/docker/app/src road2-centos -``` - -## Pour débugger le mode développement avec les sources en local -``` -docker run --name road2-centos-server --rm -it -p 8080:8080 -v $src:/home/docker/app/src road2-centos /bin/bash -``` -# Lancer les tests - -Les tests unitaires ont été écrits avec Mocha. Pour les lancer, on utilisera la commande suivante: -``` -docker run --name road2-centos-server --rm -v $src:/home/docker/app/src -v $test:/home/docker/app/test road2-centos npm run utest -``` - -# Lancer eslint - -Pour linter le code, il suffit de lancer la commande suivante: -``` -docker run --name road2-centos-server --rm -v $src:/home/docker/app/src road2-centos npm run lint -``` - -# Créer la documentation du code via jsdoc - -Le code est documenté via des commentaires. Ces commentaires peuvent être plus ou moins structurés avec des tags. L'outil jsdoc permet de générer un site web à partir de ces commentaires et de ces tags. - -Pour créer la documentation, il suffit de lancer la commande suivante: -``` -docker run --name road2-centos-server --rm -v $doc:/home/docker/app/documentation/code road2-centos npm run jsdoc -``` - -La documentation sera alors accessible dans `$doc`. diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index 5116086..916e07a 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -1,83 +1,56 @@ -FROM debian:buster-20181112 - -LABEL maintainer="IGN " -LABEL version="1.0" - -### MAJ -RUN apt-get -y update -RUN apt-get -y upgrade - -### Utilitaires -RUN apt-get -y install wget vim unzip - -### Installation des dépendances de NodeJS -RUN apt-get install -y python g++ make - -### Installation de NodeJS à partir des sources -# https://github.com/nodejs/node/blob/master/BUILDING.md#building-nodejs-on-supported-platforms -# https://github.com/nodejs/help/wiki/Installation -WORKDIR /home/docker/nodejs -RUN wget -O node-v12.14.0.tar.gz "https://nodejs.org/dist/v12.14.0/node-v12.14.0.tar.gz" -RUN tar -xzf /home/docker/nodejs/node-v12.14.0.tar.gz -RUN cd node-v12.14.0 && ./configure && make -j4 && make install - -### Installation des dépendances de OSRM -# https://github.com/Project-OSRM/osrm-backend/wiki/Building-OSRM -RUN apt-get install -y cmake libboost-dev libboost-filesystem-dev libboost-thread-dev libboost-system-dev libboost-regex-dev libstxxl-dev libxml2-dev libsparsehash-dev libbz2-dev zlib1g-dev libzip-dev libgomp1 liblua5.2-dev pkg-config libgdal-dev libboost-program-options-dev libboost-iostreams-dev libboost-test-dev libtbb-dev libexpat1-dev - -### Installation de OSRM pour utiliser la libosrm dans Road2 -# https://github.com/Project-OSRM/osrm-backend -# https://github.com/Project-OSRM/osrm-backend/blob/master/docs/nodejs/api.md -# https://github.com/door2door-io/osrm-express-server-demo -# https://github.com/Project-OSRM/osrm-backend/blob/master/CMakeLists.txt -WORKDIR /home/docker/osrm -RUN wget https://github.com/Project-OSRM/osrm-backend/archive/v5.25.0.zip -RUN unzip v5.25.0.zip -d ./osrm-backend -RUN cd /home/docker/osrm/osrm-backend/osrm-backend-5.25.0/ && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && cmake --build . && cmake --build . --target install - -### Dossier des données -WORKDIR /home/docker/internal - -### Récupération de données sur la Corse -# L'objectif est d'avoir un container indépendant du docker-compose -RUN wget download.geofabrik.de/europe/france/corse-latest.osm.pbf -RUN osrm-extract corse-latest.osm.pbf -p /usr/local/share/osrm/profiles/car.lua -RUN osrm-contract corse-latest.osrm - -### Dossier contenant la configuration +### TODO : Supprimer la compilation et l'installation de Valhalla dès que l'on aura un binding NodeJS pour lui et donc repasser sur une image node +FROM node:16-bullseye as build + +### Compilation de Valhalla +RUN apt-get update && \ +apt-get install -y cmake make libtool pkg-config g++ gcc curl unzip jq lcov protobuf-compiler \ +vim-common locales libcurl4-openssl-dev zlib1g-dev liblz4-dev libprotobuf-dev && \ +apt-get install -y libgeos-dev libgeos++-dev libluajit-5.1-dev libspatialite-dev libsqlite3-dev wget sqlite3 spatialite-bin python3-shapely && \ +apt-get install -y libsqlite3-mod-spatialite python3-pip + +WORKDIR /home/prime-server +RUN apt-get install -y git cmake autoconf automake pkg-config libtool make gcc g++ lcov libcurl4-openssl-dev libzmq3-dev libczmq-dev +RUN git clone --depth 1 --recursive https://github.com/kevinkreiser/prime_server.git && cd prime_server && \ +cmake -B build . && cmake --build build && make -C build install + +WORKDIR /home/valhalla/ +RUN pip install --upgrade conan +RUN git clone --branch 3.2.0 --depth 1 --recursive https://github.com/valhalla/valhalla.git && cd valhalla && \ +mkdir build && cmake -B build -DCMAKE_BUILD_TYPE=Release && make -C build && make -C build package + +FROM node:16-bullseye as road2 + +### Installation des dépendances pour Valhalla et prime-server +RUN apt-get update && \ + apt-get install -y libtool pkg-config curl unzip jq lcov protobuf-compiler \ + vim-common locales libcurl4-openssl-dev zlib1g-dev liblz4-dev libprotobuf-dev \ + libgeos-dev libgeos++-dev libluajit-5.1-dev libspatialite-dev libsqlite3-dev wget sqlite3 spatialite-bin python3-shapely \ + libsqlite3-mod-spatialite libzmq3-dev libczmq-dev + +### Installation prime-server +COPY --from=build /usr/local/lib/libprime_server.so.0.7.0 /usr/lib/libprime_server.so.0.0.0 +COPY --from=build /usr/local/lib/libprime_server.so.0 /usr/lib/libprime_server.so.0 +COPY --from=build /usr/local/lib/libprime_server.so /usr/lib/libprime_server.so + +### Installation de valhalla +COPY --from=build /home/valhalla/valhalla/build/valhalla-3.2.0-Linux.tar.gz ./ +RUN tar -xzvf valhalla-3.2.0-Linux.tar.gz && cd valhalla-3.2.0-Linux && cp -r bin/* /usr/bin/ && cp -r lib/* /usr/lib/ && cp -r include/* /usr/include/ && cp -r share/* /usr/share/ + +### Dossier contenant la configuration de Road2 WORKDIR /home/docker/config COPY /docker/config /home/docker/config/ ### Dossier de l'application WORKDIR /home/docker/app - -### Récupération des sources de l'application -COPY package.json ./ -COPY eslint.json ./ -COPY jsdoc.json ./ COPY src ./src/ -COPY /test ./test/ - -### Installation des dépandences de l'application NodeJS -# https://nodejs.org/en/docs/guides/nodejs-docker-webapp/ -# https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md -# https://expressjs.com/fr/starter/installing.html -RUN npm install - -### Installation de mocha pour les tests -# https://mochajs.org/#installation -RUN npm install mocha -g - -### Installation de eslint pour le code -# https://eslint.org/docs/user-guide/configuring -RUN npm install eslint -g +### Récupération des sources de l'application +COPY *.json ./ -### Installation de jsdoc pour la documentation du code -# http://usejsdoc.org/about-configuring-jsdoc.html -RUN npm install jsdoc -g +### Installation des dépendances de l'application NodeJS +RUN npm install && npm install -g mocha eslint jsdoc ### Volume partagé pour lire les données VOLUME ["/home/docker/data"] ### Commande de lancement de l'application -CMD npm run debug -- --ROAD2_CONF_FILE=/home/docker/config/road2.json +CMD npm run debug -- --ROAD2_CONF_FILE=../config/road2.json diff --git a/docker/readme.md b/docker/readme.md index 9de92b3..e221985 100644 --- a/docker/readme.md +++ b/docker/readme.md @@ -4,10 +4,10 @@ Ce dossier regroupe les différents fichiers permettant d'utiliser Road2 avec do Il y a un sous-dossier pour les grands usages identifiés : - [dev](./dev/) : développer Road2 -- [demonstration](./demonstration/) : obtenir une démonstration locale des services proposés par Road2 +- [demonstration](./demonstration/) : obtenir une démonstration locale des services proposés par Road2. Ce Dockerfile est limité aux tests des moteurs OSRM et PGRouting car il n'existe pas de bindings Valhalla pour le moment. - [test](./test/) : Tester Road2 D'autres sous-dossiers sont ordonnés ainsi pour des raisons pratiques : - [web](./web/) : Ce dossier regroupe des fichiers utiles pour avoir un petit site web qui contient plusieurs documentations et des pages de tests graphiques pour Road2. - [config](./config/) : Ce dossier regroupe plusieurs fichiers de configurations qui se trouvent être communs aux autres sous-dossiers. -- [distributions](./distributions/) : Ce dossier regroupe différents `Dockerfile` qui sont des exemples d'installation sous différentes distributions. \ No newline at end of file +- [distributions](./distributions/) : Ce dossier peut regrouper différents `Dockerfile` qui sont des exemples d'installation sous différentes distributions. Actuellement, il ne reste plus qu'un exemple pour Debian. C'est le Dockerfile préconisé pour développer sur Road2 car il contient tous les binaires utiles aux différents moteurs. \ No newline at end of file diff --git a/docker/route-graph-generator b/docker/route-graph-generator index 602f543..3d70be3 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit 602f5432ac37128b1a3069e195ad36b045d471e7 +Subproject commit 3d70be34b11b88b1a06b5c7f0477da010a28f59d diff --git a/documentation/developers/version.md b/documentation/developers/version.md index 0a4350d..f114c30 100644 --- a/documentation/developers/version.md +++ b/documentation/developers/version.md @@ -44,8 +44,8 @@ Il est conseillé de commencer par gérer les versions de ces deux là. *Ce qui Démarche à suivre pour chaque projet: 1. Tester `develop` et corriger si nécessaire. -2. Merge de `develop` sur `master`. -3. Update de la version sur `master` à 1.0.1. +2. Update de la version sur `master` à 1.0.1. +3. Merge de `develop` sur `master`. 4. Update de la version sur `develop` à 1.0.2-DEVELOP. 5. Faire des tests sur `master` et corriger si nécessaire. 6. S'il y a eu des corrections sur `master`, alors faire un merge de `master` sur `develop` et recommencer à 1. en changeant le numéro de version. @@ -63,8 +63,8 @@ Démarche à suivre pour Road2: 0. Réaliser les montée de version et les merge sur Route Graph Generator et PGRouting Procedures. 1. Tester `develop` avec les `develop` des autres projets, et corriger si nécessaire. -2. Merge de `develop` sur `master`. -3. Update de la version sur `master` à 1.0.1. +2. Update de la version sur `develop` à 1.0.1. +3. Merge de `develop` sur `master`. 4. Update de la version sur `develop` à 1.0.2-DEVELOP. 5. Faire des tests sur `master` avec les `master` des autres projets, et corriger si nécessaire. 6. S'il y a eu des corrections sur `master`, alors faire un merge de `master` sur `develop` et recommencer à 1. en changeant le numéro de version. diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index fa598af..1956332 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -376,6 +376,7 @@ module.exports = class sourceManager { try { let osrmTest = require('osrm'); } catch(error) { + LOGGER.debug(error); LOGGER.error("Le module osrm n'est pas disponible mais une source osrm est proposée dans la configuration."); return false; } diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index 490f667..c3eb438 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -17,6 +17,39 @@ Feature: Road2-PGR | GET | | POST | + Scenario Outline: [] Route sur l'API simple 1.0.0 avec un autre crs + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | crs | EPSG:2154 | + | start | 651475,6826145 | + | end | 651475,6826140 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "crs" with value "EPSG:2154" + Examples: + | method | + | GET | + | POST | + + Scenario Outline: [] Route sur l'API simple 1.0.0 avec mauvais crs + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | crs | EPSG:4325 | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "error.message" with value "Parameter 'crs' is invalid" + Examples: + | method | + | GET | + | POST | + Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" @@ -800,6 +833,39 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 | GET | | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un autre crs + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone" + And with query parameters: + | key | value | + | crs | EPSG:2154 | + | point | 651475,6826145 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "crs" with value "EPSG:2154" + Examples: + | method | + | GET | + | POST | + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec mauvais crs + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone" + And with query parameters: + | key | value | + | crs | EPSG:4325 | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "error.message" with value "Parameter 'crs' is invalid" + Examples: + | method | + | GET | + | POST | + + Scenario: [GET] Isochrone sur l'API simple 1.0.0 avec une contrainte sur une ressource pgr Given an "GET" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" From b7256a928c16af96f418338161642978e33b2d74 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 5 Dec 2022 12:09:39 +0100 Subject: [PATCH 24/93] [docker] utilisation de notre fork valhalla --- docker/distributions/debian/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index 916e07a..ab2644b 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -15,7 +15,7 @@ cmake -B build . && cmake --build build && make -C build install WORKDIR /home/valhalla/ RUN pip install --upgrade conan -RUN git clone --branch 3.2.0 --depth 1 --recursive https://github.com/valhalla/valhalla.git && cd valhalla && \ +RUN git clone --branch feature-exclude_bridges/tunnels/toll --depth 1 --recursive https://github.com/IGNF/valhalla.git && cd valhalla && \ mkdir build && cmake -B build -DCMAKE_BUILD_TYPE=Release && make -C build && make -C build package FROM node:16-bullseye as road2 @@ -33,8 +33,8 @@ COPY --from=build /usr/local/lib/libprime_server.so.0 /usr/lib/libprime_server.s COPY --from=build /usr/local/lib/libprime_server.so /usr/lib/libprime_server.so ### Installation de valhalla -COPY --from=build /home/valhalla/valhalla/build/valhalla-3.2.0-Linux.tar.gz ./ -RUN tar -xzvf valhalla-3.2.0-Linux.tar.gz && cd valhalla-3.2.0-Linux && cp -r bin/* /usr/bin/ && cp -r lib/* /usr/lib/ && cp -r include/* /usr/include/ && cp -r share/* /usr/share/ +COPY --from=build /home/valhalla/valhalla/build/valhalla-3.1.4-Linux.tar.gz ./ +RUN tar -xzvf valhalla-3.1.4-Linux.tar.gz && cd valhalla-3.1.4-Linux && cp -r bin/* /usr/bin/ && cp -r lib/* /usr/lib/ && cp -r include/* /usr/include/ && cp -r share/* /usr/share/ ### Dossier contenant la configuration de Road2 WORKDIR /home/docker/config From 8eeb766851591b03bbb2ec851180d050496325fd Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 5 Dec 2022 12:15:53 +0100 Subject: [PATCH 25/93] [deps] utilisation de r2gg 1.2.3 --- docker/route-graph-generator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/route-graph-generator b/docker/route-graph-generator index 3d70be3..d403450 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit 3d70be34b11b88b1a06b5c7f0477da010a28f59d +Subproject commit d403450fda103cc67a97a4be8889d25fe045fbd7 From 525a5b4f6dbb7e8a8a209e0cf971689aebefa8cd Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 5 Dec 2022 16:55:29 +0100 Subject: [PATCH 26/93] [test] maj des tests valhalla --- .../cucumber/configurations/local-service.json | 4 +++- .../features/req-simple-1.0.0-valhalla.feature | 12 ++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/functional/request/cucumber/configurations/local-service.json b/test/functional/request/cucumber/configurations/local-service.json index 2bbd40c..fb332bc 100644 --- a/test/functional/request/cucumber/configurations/local-service.json +++ b/test/functional/request/cucumber/configurations/local-service.json @@ -56,7 +56,9 @@ "id": "isochrone-valhalla", "parameters": { "resource": "bduni-idf-valhalla", - "point": "2.333865,48.881989" + "point": "2.333865,48.881989", + "costValue": "100", + "costType": "time" } }, { diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature index 562fced..82061f3 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature @@ -7,10 +7,6 @@ Feature: Road2-Valhalla Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource valhalla Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone-valhalla" - And with query parameters: - | key | value | - | costType | distance | - | costValue | 31000 | When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" @@ -21,13 +17,13 @@ Feature: Road2-Valhalla | GET | | POST | - Scenario Outline: [] Isochrone sur l'API simple 1.0.0 sur une ressource valhalla + Scenario Outline: [] Isochrone distance sur l'API simple 1.0.0 sur une ressource valhalla Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone-valhalla" And with query parameters: - | key | value | - | costType | distance | - | costValue | 1000 | + | key | value | + | costType | distance | + | costValue | 1000 | When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" From 1f8d42a0e876579753c0bb8d006faae1e36a66ea Mon Sep 17 00:00:00 2001 From: Loic Date: Wed, 7 Dec 2022 13:35:17 +0100 Subject: [PATCH 27/93] [deps] utilisation de pgrouting-procedures 2.0.0 --- docker/motors/pgrouting-procedures | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/motors/pgrouting-procedures b/docker/motors/pgrouting-procedures index 4336f8d..f9bee79 160000 --- a/docker/motors/pgrouting-procedures +++ b/docker/motors/pgrouting-procedures @@ -1 +1 @@ -Subproject commit 4336f8da74d76ed95971113f4d878525ca3ca2d5 +Subproject commit f9bee79dcafd4820b20132a9361cf86b58bf7264 From 538e971fc377a0827d19e3818e4e1a1a5436c3df Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 8 Dec 2022 10:40:01 +0100 Subject: [PATCH 28/93] [fix] reprojection des isochrones --- changelog.md | 5 + package.json | 2 +- .../simple/1.0.0/controller/controller.js | 8 +- src/js/geometry/polygon.js | 99 +++++++++++++++++++ src/js/requests/isochroneRequest.js | 2 +- src/js/sources/osrmSource.js | 2 +- src/js/sources/pgrSource.js | 66 +++++++++---- src/js/sources/smartroutingSource.js | 2 +- src/js/sources/valhallaSource.js | 50 +++++++--- .../request/cucumber/data/bduni/pgr/3.json | 1 + .../request/cucumber/data/bduni/pgr/4.json | 1 + .../features/req-data-bduni-pgr.feature | 35 +++++++ .../features/req-simple-1.0.0-pgr.feature | 2 +- .../req-simple-1.0.0-valhalla.feature | 19 +++- .../cucumber/features/support/world.js | 58 +++++++++++ 15 files changed, 310 insertions(+), 42 deletions(-) create mode 100644 test/functional/request/cucumber/data/bduni/pgr/3.json create mode 100644 test/functional/request/cucumber/data/bduni/pgr/4.json diff --git a/changelog.md b/changelog.md index bf11cea..accf6ff 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,9 @@ CHANGED: - Les sources ne sont plus configurées dans le même fichier que les ressources. Chaque source est configurée dans son fichier. L'ensemble est placé dans un dossier de sources. Il peut y en avoir plusieurs. - Les sources PGRouting et Valhalla ne sont plus configurées de la même manière : chaque source de ces types peut contenir plusieurs coûts. +FIXED: + - Les reprojections des isochrones fonctionnent + UPDATED: - Passage à `osrm` 5.26.0 - Passage à `pg` 8.8.0 @@ -22,6 +25,8 @@ UPDATED: - Passage à `nconf` 0.12.0 - Passage à `proj4` 2.8.0 - Utilisation de NodeJS 16 dans docker + - Passage à `pgrouting-procedures` 2.0.0 + - Passage à `route-graph-generator` 1.2.3 # 1.1.2 diff --git a/package.json b/package.json index 2582d2c..cc8710b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "1.1.3-DEVELOP", + "version": "2.0.0-DEVELOP", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", diff --git a/src/js/apis/simple/1.0.0/controller/controller.js b/src/js/apis/simple/1.0.0/controller/controller.js index 98d99b1..c75bcd0 100644 --- a/src/js/apis/simple/1.0.0/controller/controller.js +++ b/src/js/apis/simple/1.0.0/controller/controller.js @@ -714,10 +714,10 @@ module.exports = { } else { LOGGER.debug("point valide"); - //TODO: passer par la classe Point - const tmpStringCoordinates = parameters.point.split(","); - point.lon = Number(tmpStringCoordinates[0]); - point.lat = Number(tmpStringCoordinates[1]); + let tmpStringCoordinates = parameters.point.split(","); + point = new Point(Number(tmpStringCoordinates[0]), Number(tmpStringCoordinates[1]), askedProjection); + LOGGER.debug("user point in road2' object:"); + LOGGER.debug(point); } diff --git a/src/js/geometry/polygon.js b/src/js/geometry/polygon.js index e04c5d0..2c95bbd 100644 --- a/src/js/geometry/polygon.js +++ b/src/js/geometry/polygon.js @@ -4,6 +4,7 @@ const errorManager = require('../utils/errorManager'); const turf = require('@turf/turf'); const polyline = require('@mapbox/polyline'); const Geometry = require('../geometry/geometry'); +const proj4 = require('proj4'); /** * @@ -42,6 +43,17 @@ module.exports = class Polygon extends Geometry { return this._geom; } + /** + * + * @function + * @name getGeoJSON + * @description Récupérer la représentation geoJSON du polygon + * + */ + getGeoJSON () { + return this._convertGeometry(this._geom, this._format, 'geojson'); + } + /** * * @function @@ -111,5 +123,92 @@ module.exports = class Polygon extends Geometry { //TODO: voir si on peut remplacer ce throw par un return {} throw errorManager.createError("Unsupported geometry conversion"); } + + } + + + /** + * + * @function + * @name transform + * @description Reprojeter un polygon + * @param{string} projection - Projection demandée + * + */ + transform (projection) { + + if (projection === this._projection) { + return true; + } + + let geojson = this.getGeoJSON(); + + // vérifications sur le geojson à reprojeter + if (!geojson.coordinates) { + return false; + } + if (!Array.isArray(geojson.coordinates)) { + return false; + } + if (geojson.coordinates.length === 0) { + return false; + } + + for (let g = 0; g < geojson.coordinates.length; g++) { + + if (!geojson.coordinates[g]) { + return false; + } + if (!Array.isArray(geojson.coordinates[g])) { + return false; + } + if (geojson.coordinates[g].length === 0) { + return false; + } + + for (let c = 0; c < geojson.coordinates[g].length; c++) { + + if (!geojson.coordinates[g][c]) { + return false; + } + if (!Array.isArray(geojson.coordinates[g][c])) { + return false; + } + if (geojson.coordinates[g][c].length < 2) { + return false; + } + + } + + } + + // Reprojection + for (let g = 0; g < geojson.coordinates.length; g++) { + + for (let c = 0; c < geojson.coordinates[g].length; c++) { + + let reprojectedPoint = proj4(this.projection, projection, [geojson.coordinates[g][c][0], geojson.coordinates[g][c][1]]); + + if (!Array.isArray(reprojectedPoint)) { + return false; + } + if (reprojectedPoint.length !== 2) { + return false; + } + + geojson.coordinates[g][c][0] = reprojectedPoint[0]; + geojson.coordinates[g][c][1] = reprojectedPoint[1]; + + } + + } + + // TODO: gérer les différents formats de géométrie + + this._projection = projection; + + return true; + } + } diff --git a/src/js/requests/isochroneRequest.js b/src/js/requests/isochroneRequest.js index f764d28..343ef21 100644 --- a/src/js/requests/isochroneRequest.js +++ b/src/js/requests/isochroneRequest.js @@ -18,7 +18,7 @@ module.exports = class isochroneRequest extends Request { * @name constructor * @description Constructeur de la classe isochroneRequest * @param {string} resource - Ressource. - * @param {string} point - Point de départ. + * @param {Point} point - Point de départ ou d'arrivée * @param {string} costType - Type du coût. * @param {string} costValue - Valeur du coût. * @param {string} profile - Profil utilisé pour le calcul. diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index 3115c92..fdad5ea 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -567,7 +567,7 @@ module.exports = class osrmSource extends Source { // Récupération de l'ensemble des points de la réponse d'OSRM if (osrmResponse.waypoints) { - console.log(osrmResponse); + LOGGER.debug(osrmResponse); if (osrmResponse.waypoints.length < 1) { throw errorManager.createError(" OSRM response is invalid: the number of waypoints is lower than 1. "); } else { diff --git a/src/js/sources/pgrSource.js b/src/js/sources/pgrSource.js index 88c71c0..0874a52 100644 --- a/src/js/sources/pgrSource.js +++ b/src/js/sources/pgrSource.js @@ -426,7 +426,13 @@ module.exports = class pgrSource extends Source { LOGGER.debug("type of request is isochroneRequest"); - const point = [request.point.lon, request.point.lat]; + let point = ""; + try { + point = JSON.stringify(request.point.getCoordinatesIn(super.projection)); + } catch(error) { + LOGGER.error(error); + reject(errorManager.createError("Impossible de récupérer les coordonnées du point")); + } let constraints = ""; @@ -446,12 +452,11 @@ module.exports = class pgrSource extends Source { LOGGER.debug("no constraints asked"); } - const queryString = `SELECT * FROM ${this._schema}.generateIsochrone(ARRAY ${JSON.stringify(point)}, $1, $2, $3, $4, $5, $6)`; - + const queryString = `SELECT * FROM ${this._schema}.generateIsochrone(ARRAY ${point}, $1, $2, $3, $4, $5)`; + const SQLParametersTable = [ request.costValue, request.direction, - parseInt(request.askedProjection.split(':')[1]), // e.g. Transformer "EPSG:4326" en 4326 (pour PostGIS). this._costs[request.profile][request.costType].costColumn, this._costs[request.profile][request.costType].rcostColumn, constraints @@ -489,7 +494,7 @@ module.exports = class pgrSource extends Source { LOGGER.debug(result); try { - resolve(this.writeIsochroneResponse(request, pgrRequest, result)); + resolve(this.writeIsochroneResponse(request, result)); } catch (error) { reject(error); } @@ -1055,7 +1060,8 @@ module.exports = class pgrSource extends Source { * @param {pgrResponse} pgrResponse - Objet pgrResponse * */ - writeIsochroneResponse(isochroneRequest, pgrRequest, pgrResponse) { + writeIsochroneResponse(isochroneRequest, pgrResponse) { + let point = {}; let geometry = {}; @@ -1064,20 +1070,46 @@ module.exports = class pgrSource extends Source { throw errorManager.createError(" No data found ", 404); } - // Création d'un objet Point (utile plus tard). - point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, super.projection); + // Projection demandée dans la requête + let askedProjection = isochroneRequest.point.projection; + LOGGER.debug("asked projection: " + askedProjection); + + LOGGER.debug("data projection: " + super.projection); - let rawGeometry = JSON.parse(pgrResponse.rows[0].geometry); - // Cas où il n'y a pas d'isochrone car costValue trop faible + // Création d'un objet Point + point = isochroneRequest.point; + if (!point.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of point in PGRouting response"); + } else { + LOGGER.debug("point in asked projection:"); + LOGGER.debug(point); + } + + let rawGeometry = null; + + try { + rawGeometry = JSON.parse(pgrResponse.rows[0].geometry); + } catch(error) { + LOGGER.debug(error); + throw errorManager.createError("Impossible de parser la réponse de PGRouting"); + } + + // Création d'un objet Polygon à partir de la géométrie brute if (rawGeometry === null) { - rawGeometry = { - type: 'Point', - coordinates: [ - isochroneRequest.point.lon, - isochroneRequest.point.lat - ] - }; + // Potentiellement le cas où il n'y a pas d'isochrone car costValue trop faible + geometry = new Polygon({type: 'Point',coordinates: [point.x,point.y]}, "geojson", askedProjection); + } else { + + geometry = new Polygon(rawGeometry, "geojson", super.projection); + + if (!geometry.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of point in PGRouting response"); + } else { + LOGGER.debug("point in asked projection:"); + LOGGER.debug(point); + } + } // Création d'un objet Polygon à partir du GeoJSON reçu. diff --git a/src/js/sources/smartroutingSource.js b/src/js/sources/smartroutingSource.js index 31733d3..0d179d0 100644 --- a/src/js/sources/smartroutingSource.js +++ b/src/js/sources/smartroutingSource.js @@ -195,7 +195,7 @@ module.exports = class smartroutingSource extends Source { // Coordonnées // location - smartroutingRequest.location = request.point.lon + "," + request.point.lat; + smartroutingRequest.location = request.point.x + "," + request.point.y; // optimization const mapMethods = { diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js index 1413082..1336f06 100644 --- a/src/js/sources/valhallaSource.js +++ b/src/js/sources/valhallaSource.js @@ -270,11 +270,15 @@ module.exports = class valhallaSource extends Source { reverse = "true"; } - const locationsString = `"locations":[{"lat":${request.point.lat},"lon":${request.point.lon}}]`; + // Reprojection du point si nécessaire + let tmpPoint = request.point.getCoordinatesIn(super.projection); + + const locationsString = `"locations":[{"lat":${tmpPoint[1]},"lon":${tmpPoint[0]}}]`; const costingString = `"costing":"${this._costs[request.profile][request.costType].costing}"`; const contoursString = `"contours":[{"${request.costType}":${costValue}}]`; const reverseString = `"reverse":${reverse}`; - const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${contoursString},${reverseString}}' `; + const polygonsString = `"polygons":true`; + const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${contoursString},${reverseString},${polygonsString}}' `; LOGGER.info(commandString); return new Promise( (resolve, reject) => { @@ -538,6 +542,7 @@ module.exports = class valhallaSource extends Source { * */ writeIsochroneResponse(isochroneRequest, valhallaResponseStr) { + let point = {}; let geometry = {}; let valhallaResponse; @@ -557,24 +562,39 @@ module.exports = class valhallaSource extends Source { throw errorManager.createError(" No data found ", 404); } - // Création d'un objet Point (utile plus tard). - point = new Point(isochroneRequest.point.lon, isochroneRequest.point.lat, super.projection); + // Projection demandée dans la requête + let askedProjection = isochroneRequest.point.projection; + LOGGER.debug("asked projection: " + askedProjection); + + LOGGER.debug("data projection: " + super.projection); + + // Création d'un objet Point + point = isochroneRequest.point; + if (!point.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of point in Valhalla response"); + } else { + LOGGER.debug("point in asked projection:"); + LOGGER.debug(point); + } let rawGeometry = valhallaResponse.features[0].geometry; - // Cas où il n'y a pas d'isochrone car costValue trop faible + // Création d'un objet Polygon à partir de la géométrie brute if (rawGeometry === null) { - rawGeometry = { - type: 'Point', - coordinates: [ - isochroneRequest.point.lon, - isochroneRequest.point.lat - ] - }; - } + // Potentiellement le cas où il n'y a pas d'isochrone car costValue trop faible + geometry = new Polygon({type: 'Point',coordinates: [point.x,point.y]}, "geojson", askedProjection); + } else { + + geometry = new Polygon(rawGeometry, "geojson", super.projection); + + if (!geometry.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of point in Valhalla response"); + } else { + LOGGER.debug("point in asked projection:"); + LOGGER.debug(point); + } - // Création d'un objet Polygon à partir du GeoJSON reçu. - geometry = new Polygon(rawGeometry, "geojson", super.projection); + } /* Envoi de la réponse au proxy. */ return new IsochroneResponse( diff --git a/test/functional/request/cucumber/data/bduni/pgr/3.json b/test/functional/request/cucumber/data/bduni/pgr/3.json new file mode 100644 index 0000000..91e4224 --- /dev/null +++ b/test/functional/request/cucumber/data/bduni/pgr/3.json @@ -0,0 +1 @@ +{"point":"2.1,48.7","resource":"bduni-idf-pgr","resourceVersion":"2022-11-24","costType":"time","costValue":100,"timeUnit":"second","profile":"car","direction":"departure","crs":"EPSG:4326","geometry":{"type":"Polygon","coordinates":[[[2.093394624,48.701919765],[2.09343569,48.701908761],[2.09343622,48.701908617],[2.093436682,48.70190849],[2.093437221,48.701908339],[2.093466369,48.701900075],[2.09348427,48.701892432],[2.093498626,48.701884004],[2.093514024,48.7018721],[2.093754753,48.70163137],[2.094337538,48.701442596],[2.094470663,48.701478266],[2.094499035,48.701485869],[2.094685301,48.701535779],[2.094695136,48.701537735],[2.094703691,48.701538861],[2.094713698,48.701539517],[2.094721342,48.701539517],[2.09473135,48.701538861],[2.094739904,48.701537735],[2.094749739,48.701535779],[2.094776915,48.701528497],[2.094777446,48.701528353],[2.094777907,48.701528226],[2.094778446,48.701528075],[2.095146237,48.701423799],[2.095164137,48.701416156],[2.095178493,48.701407728],[2.095193891,48.701395824],[2.09543331,48.701156405],[2.095763452,48.701108003],[2.095919343,48.701149774],[2.095948831,48.701157675],[2.095958666,48.701159632],[2.095967221,48.701160758],[2.095977228,48.701161414],[2.095984872,48.701161414],[2.095994879,48.701160758],[2.096003434,48.701159632],[2.096013269,48.701157675],[2.096396591,48.701054965],[2.096406086,48.701051741],[2.096414058,48.701048439],[2.096423053,48.701044004],[2.096429673,48.701040182],[2.096438011,48.70103461],[2.096444856,48.701029358],[2.096452395,48.701022746],[2.096478025,48.700997116],[2.096543099,48.700932042],[2.096666937,48.700965225],[2.096676772,48.700967181],[2.096685327,48.700968307],[2.096695334,48.700968963],[2.096702978,48.700968963],[2.096712986,48.700968307],[2.09672154,48.700967181],[2.096731375,48.700965225],[2.097114697,48.700862514],[2.097124193,48.700859291],[2.097131265,48.700856361],[2.097209849,48.700934946],[2.097124752,48.700957748],[2.097115256,48.700960971],[2.097107284,48.700964273],[2.09709829,48.700968708],[2.097089734,48.700973648],[2.097081395,48.70097922],[2.09707455,48.700984472],[2.09706701,48.700991084],[2.096968677,48.701089418],[2.0967864,48.701271695],[2.096784337,48.701273839],[2.096782595,48.701275722],[2.096780618,48.701277944],[2.096778693,48.701280193],[2.096776803,48.701282491],[2.096775213,48.701284503],[2.096773413,48.701286872],[2.094811931,48.70397616],[2.094811118,48.703977296],[2.094810426,48.703978282],[2.094809634,48.703979433],[2.094552817,48.704359921],[2.094930291,48.705155209],[2.095776453,48.705367512],[2.09578059,48.705360604],[2.095783428,48.70535001],[2.096111177,48.705356264],[2.096119489,48.705355971],[2.096127563,48.705355247],[2.096135794,48.705354056],[2.096155817,48.705350039],[2.09622066,48.705428628],[2.096223057,48.705431396],[2.096225461,48.70543404],[2.096227987,48.705436691],[2.09627863,48.705487333],[2.096282599,48.705494505],[2.096459541,48.7055389],[2.09717789,48.705719135],[2.098991328,48.705535789],[2.099587807,48.70539902],[2.099657249,48.705468462],[2.101672382,48.705264724],[2.1026834,48.705162506],[2.103512697,48.704927926],[2.103513956,48.704909944],[2.103513791,48.704897245],[2.103512607,48.704884949],[2.103510347,48.704872451],[2.103476111,48.704744678],[2.103474606,48.704740245],[2.103541113,48.704492035],[2.103542674,48.704484849],[2.103543857,48.704477779],[2.103544722,48.704470476],[2.103551768,48.704369828],[2.103551602,48.704357129],[2.103550419,48.704344833],[2.103548159,48.704332336],[2.103533579,48.704277922],[2.104247576,48.704083537],[2.104251783,48.704084719],[2.104256536,48.704085887],[2.104261444,48.704086924],[2.104456817,48.704121576],[2.104466784,48.704122678],[2.104476541,48.704123114],[2.104486567,48.704122905],[2.104495799,48.704122107],[2.104505712,48.704120591],[2.104515249,48.704118487],[2.104520489,48.704116965],[2.104520509,48.704117037],[2.105044629,48.703971192],[2.105144203,48.704013419],[2.105148884,48.704015226],[2.105153504,48.704016838],[2.105158293,48.704018334],[2.105162796,48.704019581],[2.105167672,48.704020762],[2.105172464,48.704021757],[2.105177407,48.704022615],[2.105380766,48.704051125],[2.105385754,48.704051659],[2.105390634,48.70405202],[2.105395647,48.704052226],[2.10540032,48.704052265],[2.105405335,48.704052143],[2.105410221,48.704051864],[2.105415218,48.704051414],[2.105604424,48.704028118],[2.106129746,48.704025672],[2.106294665,48.704141],[2.106458636,48.704094618],[2.106896352,48.703970803],[2.107250437,48.703847523],[2.107840677,48.703582989],[2.108126348,48.703174259],[2.108121282,48.703168749],[2.108093144,48.703149386],[2.107978541,48.703103868],[2.107931626,48.703085234],[2.107926906,48.703083536],[2.107922788,48.703082205],[2.107917966,48.703080819],[2.107913874,48.703079787],[2.107908973,48.703078719],[2.107904716,48.703077937],[2.107899755,48.703077192],[2.107818846,48.703067742],[2.107775328,48.703062659],[2.106927424,48.702222709],[2.106893964,48.702153215],[2.106871354,48.702106257],[2.10726513,48.700696292],[2.107266494,48.700695613],[2.107273957,48.700691266],[2.107282273,48.700685662],[2.107288892,48.700680542],[2.107296406,48.700673901],[2.107302489,48.70066777],[2.107309072,48.700660205],[2.107327531,48.700635955],[2.107328156,48.700634536],[2.107411876,48.70052455],[2.107428965,48.700502101],[2.107434504,48.700493741],[2.107438793,48.700486245],[2.107443193,48.700477234],[2.107446295,48.700469664],[2.107449481,48.700460156],[2.107451684,48.700451805],[2.107453603,48.700441962],[2.107463872,48.700361557],[2.108842734,48.69998449],[2.108879287,48.700021042],[2.108926663,48.700068418],[2.108934203,48.70007503],[2.108941054,48.700080288],[2.108949392,48.700085859],[2.108956477,48.700089949],[2.108965471,48.700094385],[2.10897345,48.70009769],[2.108982946,48.700100913],[2.109150807,48.700145891],[2.109337584,48.700195938],[2.109356102,48.700198539],[2.109372119,48.700198815],[2.109390716,48.700196856],[2.10942349,48.700189277],[2.109424777,48.700188967],[2.109425885,48.70018869],[2.109427167,48.700188359],[2.109810489,48.700085648],[2.109825617,48.70007982],[2.109837999,48.700073473],[2.109851564,48.700064594],[2.109974265,48.699964241],[2.109977167,48.699961745],[2.109979616,48.699959529],[2.109982389,48.699956891],[2.109987353,48.699951927],[2.110103845,48.699835435],[2.110151473,48.699787807],[2.110158085,48.699780268],[2.110163342,48.699773416],[2.110168913,48.699765078],[2.110173004,48.699757993],[2.110177439,48.699748999],[2.110180744,48.69974102],[2.110183968,48.699731524],[2.110222569,48.699587462],[2.1102513,48.699480235],[2.110276244,48.699387142],[2.11074869,48.698999107],[2.110787302,48.699002776],[2.110788853,48.699002907],[2.110790192,48.699003007],[2.110791745,48.699003107],[2.110793026,48.699003177],[2.110794581,48.699003245],[2.110795923,48.69900329],[2.110797479,48.699003327],[2.110819442,48.699003625],[2.110819735,48.699003672],[2.110823828,48.699004188],[2.110828823,48.699004653],[2.110833143,48.699004913],[2.110838158,48.699005051],[2.111041056,48.699003967],[2.111129137,48.698877945],[2.111398056,48.698493182],[2.11098788,48.698007504],[2.110944895,48.697995528],[2.110928894,48.697992852],[2.110446836,48.697964035],[2.11043843,48.697963994],[2.110431195,48.697964356],[2.110422835,48.697965236],[2.110198586,48.698001356],[2.110179154,48.698007206],[2.11016334,48.698014367],[2.110146124,48.698025111],[2.110126337,48.698041464],[2.110011697,48.698136206],[2.110004402,48.698143087],[2.10999852,48.69814941],[2.109992185,48.698157184],[2.109987442,48.69816385],[2.109982176,48.698172384],[2.109978131,48.698180014],[2.109974024,48.698189163],[2.109956982,48.698234917],[2.109363022,48.698428662],[2.109024214,48.698519445],[2.108565669,48.698328705],[2.108601073,48.698180713],[2.10860315,48.698160808],[2.108602688,48.698143689],[2.108599541,48.698123925],[2.108543035,48.697933719],[2.10853488,48.697915442],[2.10852592,48.697900848],[2.108513313,48.697885304],[2.108369274,48.697748835],[2.10836162,48.697742472],[2.108354685,48.697737432],[2.10834627,48.697732115],[2.108299006,48.697706383],[2.108289834,48.697702147],[2.108281713,48.697699028],[2.108272064,48.697696035],[2.108129615,48.697661956],[2.108124701,48.697660949],[2.108120435,48.69766022],[2.108115465,48.697659536],[2.108111822,48.697659157],[2.108106818,48.6976588],[2.108102493,48.697658633],[2.108097477,48.697658604],[2.107966969,48.697662126],[2.107961961,48.697662425],[2.107957652,48.697662825],[2.107952674,48.697663451],[2.107949058,48.697664027],[2.107944132,48.697664977],[2.107939912,48.697665936],[2.107938933,48.697666192],[2.107933837,48.697665888],[2.1079252,48.697665938],[2.107915198,48.697666652],[2.107908004,48.697667641],[2.10789818,48.697669655],[2.10788985,48.697671939],[2.107880373,48.697675218],[2.107862025,48.697682943],[2.107841828,48.697688644],[2.10782011,48.697691396],[2.107810265,48.697693305],[2.107801912,48.6976955],[2.107792401,48.697698678],[2.107785678,48.697701424],[2.107776663,48.697705816],[2.107769163,48.697710098],[2.107760798,48.697715629],[2.107757786,48.697717917],[2.107750216,48.697722119],[2.107744853,48.697723713],[2.107735448,48.697727191],[2.107727561,48.69773071],[2.10771869,48.697735386],[2.107712501,48.697739185],[2.107704315,48.69774498],[2.107697608,48.697750421],[2.107695166,48.697752682],[2.107685452,48.697758074],[2.107678706,48.697762292],[2.107673088,48.697766223],[2.107666815,48.697771118],[2.107662445,48.697774908],[2.107656713,48.697780426],[2.107652025,48.697785432],[2.107646896,48.697791514],[2.107645461,48.697793407],[2.10727196,48.697670993],[2.10718659,48.697352389],[2.107183367,48.697342893],[2.107180062,48.697334914],[2.107175627,48.697325921],[2.107171996,48.697319632],[2.107166424,48.697311293],[2.107161167,48.697304441],[2.107154555,48.697296902],[2.106873944,48.697016291],[2.106866404,48.697009679],[2.106859553,48.697004422],[2.106851214,48.69699885],[2.106844925,48.696995219],[2.106835931,48.696990784],[2.106827953,48.696987479],[2.106818456,48.696984255],[2.106592895,48.696923816],[2.106589014,48.696897978],[2.10659278,48.696864124],[2.106577835,48.696740493],[2.106581844,48.696676684],[2.106581817,48.696666656],[2.106581218,48.696657889],[2.106579882,48.69664795],[2.106577916,48.696638205],[2.106575295,48.696628526],[2.106572448,48.696620213],[2.106568585,48.696610958],[2.106504065,48.696481015],[2.106841358,48.695464689],[2.106842271,48.695464379],[2.106954012,48.695426422],[2.106963276,48.695422582],[2.106971156,48.695418694],[2.10697984,48.695413678],[2.106988105,48.695408154],[2.106996059,48.695402047],[2.107002665,48.695396252],[2.107009754,48.69538916],[2.107051176,48.695341907],[2.107051176,48.695341907],[2.107140552,48.695239951],[2.107146655,48.695231994],[2.107151535,48.695224687],[2.107156547,48.695216001],[2.107160942,48.695207084],[2.107164778,48.695197818],[2.107167601,48.695189497],[2.107170194,48.69517981],[2.107208864,48.694985193],[2.107210171,48.69497525],[2.107210744,48.694966482],[2.107210741,48.694956453],[2.107210089,48.694946533],[2.107208778,48.694936591],[2.107207062,48.694927974],[2.107204465,48.694918288],[2.10719541,48.694891633],[2.107156364,48.694776684],[2.107398822,48.694193684],[2.107495528,48.694167772],[2.107505024,48.694164548],[2.107513142,48.694161186],[2.107522136,48.69415675],[2.107530745,48.69415178],[2.107539083,48.694146208],[2.107546055,48.694140859],[2.107553594,48.694134247],[2.107571086,48.694116755],[2.107589104,48.694088247],[2.107615428,48.694014271],[2.107078681,48.693378723],[2.106024417,48.693797191],[2.105269772,48.694096732],[2.104609936,48.695270227],[2.101379131,48.695712306],[2.101376748,48.69571267],[2.10137466,48.695713023],[2.101372289,48.695713462],[2.101369888,48.695713946],[2.101367533,48.69571446],[2.101365471,48.695714943],[2.101363133,48.69571553],[2.101238428,48.695748945],[2.10123776,48.695749124],[2.101232247,48.695750826],[2.10122747,48.6957525],[2.101222101,48.695754612],[2.101110728,48.695803328],[2.100984853,48.695837056],[2.100966363,48.695844715],[2.100951274,48.695853427],[2.100935396,48.695865611],[2.100766016,48.696034991],[2.10059069,48.695988012],[2.100570847,48.6959854],[2.100553424,48.6959854],[2.100533582,48.695988012],[2.100445573,48.696011594],[2.10005244,48.69616764],[2.099820192,48.696399888],[2.099808009,48.696415765],[2.099800984,48.696427933],[2.09972605,48.696407854],[2.099706208,48.696405242],[2.099688784,48.696405242],[2.099668942,48.696407854],[2.09928562,48.696510565],[2.09926713,48.696518224],[2.099252041,48.696526936],[2.099236163,48.696539119],[2.098955553,48.69681973],[2.098943369,48.696835608],[2.098934657,48.696850697],[2.098926999,48.696869187],[2.098824288,48.697252508],[2.098821676,48.697272351],[2.098821676,48.697289774],[2.098824288,48.697309617],[2.098866086,48.697465608],[2.098863945,48.697465889],[2.098548529,48.697550405],[2.098480797,48.697297627],[2.098473139,48.697279137],[2.098464427,48.697264048],[2.098452243,48.69724817],[2.098171632,48.696967559],[2.098155755,48.696955376],[2.098140665,48.696946664],[2.098122175,48.696939005],[2.098114372,48.696936914],[2.096826139,48.697448251],[2.096216213,48.69804028],[2.096217133,48.698041663],[2.096343863,48.698168393],[2.096359692,48.698227466],[2.096367351,48.698245956],[2.096375905,48.698260773],[2.096388089,48.698276651],[2.096604933,48.698493495],[2.096643409,48.698637089],[2.096651067,48.698655579],[2.096659622,48.698670396],[2.096671806,48.698686274],[2.096952416,48.698966884],[2.096964615,48.698976245],[2.097049195,48.699291901],[2.096727553,48.699205717],[2.096707711,48.699203105],[2.096690602,48.699203105],[2.09667076,48.699205717],[2.096620344,48.699219226],[2.096287438,48.699308428],[2.096258903,48.699316075],[2.096240415,48.699323734],[2.096225599,48.699332289],[2.096209723,48.699344471],[2.096137108,48.699417086],[2.096037938,48.699390514],[2.096018095,48.699387901],[2.096000984,48.699387902],[2.09598114,48.699390515],[2.095569327,48.69950088],[2.095568261,48.699501174],[2.095567341,48.699501435],[2.095566275,48.699501745],[2.095536472,48.699510664],[2.095519161,48.699518279],[2.095505264,48.699526572],[2.095490344,48.699538194],[2.095229094,48.699799444],[2.095222482,48.699806983],[2.09521723,48.699813828],[2.095211658,48.699822167],[2.095207836,48.699828787],[2.0952034,48.699837781],[2.095200098,48.699845753],[2.095196875,48.699855249],[2.095186306,48.699894692],[2.095181327,48.699890325],[2.095174481,48.699885072],[2.095166142,48.6998795],[2.095159522,48.699875678],[2.095150528,48.699871243],[2.095142557,48.699867941],[2.095133061,48.699864718],[2.094751722,48.699762538],[2.09473077,48.699759934],[2.09471274,48.699760195],[2.094691871,48.699763405],[2.094329185,48.699871937],[2.093925085,48.700264178],[2.093914323,48.700304342],[2.093890846,48.700391962],[2.093888728,48.700399866],[2.093735045,48.700448642],[2.093555396,48.70062302],[2.093023972,48.70113885],[2.093346794,48.701818993],[2.093394624,48.701919765]]]},"constraints":[]} \ No newline at end of file diff --git a/test/functional/request/cucumber/data/bduni/pgr/4.json b/test/functional/request/cucumber/data/bduni/pgr/4.json new file mode 100644 index 0000000..5ff7d8e --- /dev/null +++ b/test/functional/request/cucumber/data/bduni/pgr/4.json @@ -0,0 +1 @@ +{"point":"2.1,48.7","resource":"bduni-idf-pgr","resourceVersion":"2022-11-24","costType":"time","costValue":100,"timeUnit":"second","profile":"car","direction":"departure","crs":"EPSG:4326","geometry":"_bghHuzwK@I??????@E@A@C@An@o@d@uBGYAEIe@?A???A?A?A?A?A@E??????TiA?A@C@An@o@HaAG_@AE?A?A?A???A?A?ATmA?A??@A?A@A??@ABELKGY?A?A?A???A?A?ATkA?A?AMOEP???@A@?@A@?@A?SRc@b@?@A?????????A@yOfK??????kAr@_DkAi@iD@?@?AaA?A?A@A?COK???AA?II??Ic@c@oCb@iJZwBMMh@qKRiEl@eDB?@?B?@?XD?@p@M@???@?RA@?B?@?HBf@oC??AA??Eg@?A?A?A?A?A?A????\\gBGSAA???A???A???AEg@?A???A???A???ABc@?iBU_@Ha@VwAVeAt@uBpAy@?@BDHT@H@??@???@???@??@N@FfDhDLFFBxGoA??@??A@A@A??@ABC@?TOBC@??A@?@A@?@?@?NAjAsGGGIIA??AAA?A?AA??AIa@Ie@?C?A?C@E???A??RkA@C@A@ARW?A????@ATUHI@A@??A@?@A@?@?ZGTEPElA}A?G???????????A???C?????A??AA@g@VQlAu@~ApA@H@@D~A?@?@A@Ej@AB?BC@ABSV?@A?A@??A@A?A@G@g@vBQbAd@xA\\EB?B?B?d@J@@B?@BZZ@@?@@?BH@@?@?@FZ?@?????@???@???X?@???@???@A????@???@?@?@?@A@?BAB?B?@A@?@??A@?@A@???@?@A??@A@?@?@A???A@?@A@???@A?A@????VjA~@N@@@???@@@?@@?@v@v@@?@@?@?@@??@@@Jl@B?F?V@J?@?@?@?@?@?@@@?XLjEcA??DU@A?A@A?A@A??@AHG??RQ@A@??A@?@?@A@?d@G@?@?@?@?@?@?@@D?TFtBo@BS@A???A@A?A@A@?@CDCNE|BjBsArE{@tCiFbCwAdS???@????????A@EV???@???@ITGX?@ABC@_@`@Fb@?B?B?BCN_@nAm@l@C@A@BL?B?B?BSjAABABA@w@v@CBA@C?kATC?C?C?_@I?@O|@p@LB@B@@@v@v@@@@B@B?@eB~FuBxB??YWKCCAAACAi@i@]GCAAACAw@w@AA}@QN~@@B?BABAHQ`AAD?BA@ABOLDR?B?B?BUpA??????ADABA@ABs@r@A@??A@A?A@A?A?E@?@?@@??@@@?@@@RjA?B?B?BUfAmAnAGBQBA?I\\a@b@gBjBgCaASG","constraints":[]} \ No newline at end of file diff --git a/test/functional/request/cucumber/features/req-data-bduni-pgr.feature b/test/functional/request/cucumber/features/req-data-bduni-pgr.feature index 53ac3e1..8a144b8 100644 --- a/test/functional/request/cucumber/features/req-data-bduni-pgr.feature +++ b/test/functional/request/cucumber/features/req-data-bduni-pgr.feature @@ -83,3 +83,38 @@ Feature: Road2 with Bduni data via PGR | method | | GET | | POST | + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone" + And with query parameters: + | key | value | + | point | 2.1,48.7 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the iso should be similar to "../../data/bduni/pgr/3.json" + + Examples: + | method | + | GET | + | POST | + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un geometryFormat polyline + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone" + And with query parameters: + | key | value | + | point | 2.1,48.7 | + | geometryFormat | polyline | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the iso should be similar to "../../data/bduni/pgr/4.json" + + Examples: + | method | + | GET | + | POST | \ No newline at end of file diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index c3eb438..3a511fa 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -843,7 +843,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" - And the response should contain a complete and valid road + And the response should contain a complete and valid iso And the response should contain an attribute "crs" with value "EPSG:2154" Examples: | method | diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature index 82061f3..3cb0a4e 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature @@ -23,7 +23,7 @@ Feature: Road2-Valhalla And with query parameters: | key | value | | costType | distance | - | costValue | 1000 | + | costValue | 2000 | When I send the request Then the server should send a response with status 200 And the response should have an header "content-type" with value "application/json" @@ -34,6 +34,23 @@ Feature: Road2-Valhalla | GET | | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un autre crs + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-valhalla" + And with query parameters: + | key | value | + | crs | EPSG:2154 | + | point | 651475,6826145 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the response should contain an attribute "crs" with value "EPSG:2154" + Examples: + | method | + | GET | + | POST | + Scenario Outline: [] Route sur l'API simple 1.0.0 avec valhalla Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-valhalla" diff --git a/test/functional/request/cucumber/features/support/world.js b/test/functional/request/cucumber/features/support/world.js index 5d40a0c..0f3002c 100644 --- a/test/functional/request/cucumber/features/support/world.js +++ b/test/functional/request/cucumber/features/support/world.js @@ -5,6 +5,7 @@ const turf = require('@turf/turf'); const axios = require('axios'); const tunnel = require('tunnel'); const https = require('https'); +const polyline = require('@mapbox/polyline'); /** @@ -704,7 +705,64 @@ class road2World { } + checkIsoContent(filePath) { + let referenceResponse = {}; + let responseJSON = {}; + + + if (typeof this._response === "string") { + responseJSON = JSON.parse(this._response); + } else { + responseJSON = this._response; + } + + try { + referenceResponse = JSON.parse(fs.readFileSync(path.resolve(__dirname,filePath))); + } catch (error) { + return "Can't parse JSON file"; + } + + let refIso = referenceResponse.geometry; + let curIso = this._response.geometry; + + if (typeof(refIso) === "string") { + + if (refIso === curIso) { + return true; + } + + // On part du principe que c'est du polyline + refIso = turf.polygon(polyline.decode(refIso)); + curIso = turf.polygon(polyline.decode(curIso)); + + } else { + + // On part du principe que c'est du GeoJSON + if (curIso.coordinates) { + + refIso = turf.polygon(refIso.coordinates); + try { + curIso = turf.polygon(curIso.coordinates); + } catch(error) { + return "Can't convert curIso into polygon with turfJS"; + } + + } else { + return "curIso doesn't have coordinates"; + } + + } + + let buffer = turf.buffer(refIso, 0.1, {units: 'kilometers'}); + + if (!turf.booleanWithin(curIso, buffer)) { + return "Iso is out of the buffer"; + } else { + return true; + } + + } } From 532153148a838b23216b7d3232460a8b8d1582a2 Mon Sep 17 00:00:00 2001 From: lgrd Date: Thu, 8 Dec 2022 14:16:39 +0100 Subject: [PATCH 29/93] Create issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..6bdd13a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve Road2 +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Type of use (choose between):** +- `service` provided by Road2 (ex. bug with a request from the API simple/1.0.0) +- `administration` of the services provided by Road2 +- `other`, please add a helpful description + +**Type of bug (choose between):** +- `request`, for bugs with an API +- `load`, for bugs during the creation of the server +- `checkConfig`, for bugs with this option + +**To Reproduce** +Steps to reproduce the behavior: +1. Load server with this configuration +2. Request the server + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Environment (please complete the following information):** + - OS: [e.g. Debian] + - OS Version: [e.g. 11] + - Node version: [e.g. 16] + - Road2 commit: [sha] + +**Configuration:** +- the configuration was generated inside the docker image: [eg. yes/no] +- if no, please, provide us the any differences you find useful + +**Data:** +Type of data: [eg. osrm, pgrouting, valhalla] +Generation of the data: [eg. docker image of route-graph-generator, other] + +**Additional context** +Add any other context about the problem here. From 7ea110a4f4283bb4b59522586f566c6a7fdf3e20 Mon Sep 17 00:00:00 2001 From: lgrd Date: Thu, 8 Dec 2022 15:06:03 +0100 Subject: [PATCH 30/93] Create issue templates --- .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From f58898647dc493a6da0573c858fe56103eb147ee Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 8 Dec 2022 16:50:53 +0100 Subject: [PATCH 31/93] [doc] roadmap sur github --- documentation/developers/roadmap.md | 12 ------------ readme.md | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 documentation/developers/roadmap.md diff --git a/documentation/developers/roadmap.md b/documentation/developers/roadmap.md deleted file mode 100644 index 7138fdb..0000000 --- a/documentation/developers/roadmap.md +++ /dev/null @@ -1,12 +0,0 @@ -# Roadmap du projet Road2 - -## Développements en cours - -Pour la fin de l'année 2022 : - -- Intégration de Valhalla dans Road2 et Route-graph-generator -- Refactorisation d'une partie du code pour : - - une meilleure gestion de la configuration - - la création d'un processus administrateur du service. Ce nouveau processus portera l'API admin sans impacter les requêtes des utilisateurs classiques du service. - - diff --git a/readme.md b/readme.md index ef6fb6a..5b4c809 100644 --- a/readme.md +++ b/readme.md @@ -86,7 +86,7 @@ Les participations à ce projet sont encouragées. Il vous est demandé de réal On trouvera une documentation dédiée aux développeurs [ici](./documentation/developers/readme.md). Elle indique les concepts utiles pour effectuer des développements sur Road2. -Pour en savoir plus sur notre roadmap, vous pouvez regarder ce [document](./documentation/developers/roadmap.md). +Pour en savoir plus sur notre roadmap, vous pouvez regarder le projet [IGNF/Road2 Roadmap](https://github.com/orgs/IGNF/projects/3). Enfin, il est possible d'utiliser ce [docker-compose](./docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. From 39100c3cc22f806afdc39f93c51a80f6c47d6d57 Mon Sep 17 00:00:00 2001 From: Loic Date: Wed, 14 Dec 2022 18:01:35 +0100 Subject: [PATCH 32/93] [feat] creation du module wkt interne --- src/js/geometry/formats/wkt.js | 340 +++++++++++++++++++ test/unit/mocha/geometry/formats/testsWkt.js | 182 ++++++++++ 2 files changed, 522 insertions(+) create mode 100644 src/js/geometry/formats/wkt.js create mode 100644 test/unit/mocha/geometry/formats/testsWkt.js diff --git a/src/js/geometry/formats/wkt.js b/src/js/geometry/formats/wkt.js new file mode 100644 index 0000000..6d01404 --- /dev/null +++ b/src/js/geometry/formats/wkt.js @@ -0,0 +1,340 @@ +'use strict'; + +module.exports = { + + + /** + * + * @function + * @name validateWKT + * @description Vérification d'un WKT + * @param {string} wktString - WKT à convertir + * @return {boolean|string} + * + */ + + validateWKT: function(wktString) { + + if (!wktString) { + return "L'argument est vide"; + } + if (typeof(wktString) !== "string") { + return "L'argument n'est pas une chaîne"; + } + + // Taille minimal d'un WKT (ex. 'POINT(2 2)') + if (wktString.length < 10) { + return "La chaine est trop courte"; + } + + // On nettoie la chaîne pour faciliter les vérifications qui suivront + let cleanWKT = this.cleanWKT(wktString); + + // On vérifie un mimimum de contenu + let splitWkt = cleanWKT.match(/^(SRID:\d+;)?(POINT|LINESTRING|POLYGON|GEOMETRYCOLLECTION)\((.*)\)$/); + if (splitWkt === null) { + return "La chaine nettoyee ne peut etre splittee"; + } + if (splitWkt.length < 3) { + return "Le split de la chaine n'est pas valide: " + splitWkt; + } + + // On récupère la partie qu'il reste à analyser + let geometryType = ""; + let geometry = ""; + if (splitWkt.length === 4) { + // il y a un SRID + geometryType = splitWkt[2]; + geometry = splitWkt[3]; + } else { + geometryType = splitWkt[1]; + geometry = splitWkt[2]; + } + + if (geometryType === "POINT") { + if (this._validatePointGeometry(geometry)) { + return true; + } + } else if (geometryType === "LINESTRING") { + if (this._validateLinestringGeometry(geometry)) { + return true; + } + } else if (geometryType === "POLYGON") { + if (this._validatePolygonGeometry(geometry)) { + return true; + } + } else if (geometryType === "GEOMETRYCOLLECTION") { + return this._validateGeometryCollection(geometry); + } else { + return "geometryType est inconnu: " + geometryType; + } + + return "La validation a echoue"; + + }, + + /** + * + * @function + * @name cleanWKT + * @description Nettoyage d'un WKT + * @param {string} wktString - WKT à convertir + * @return {string} cleanedWktString - WKT nettoyé + * + */ + + cleanWKT: function(wktString) { + + let tmpWKT = wktString.replaceAll(/\s{2,}/g,' '); + + tmpWKT = tmpWKT.replace(/^\s/,''); + tmpWKT = tmpWKT.replace(/\s$/,''); + tmpWKT = tmpWKT.replaceAll(/\s,/g,','); + tmpWKT = tmpWKT.replaceAll(/,\s/g,','); + tmpWKT = tmpWKT.replaceAll(/\(\s/g,'('); + tmpWKT = tmpWKT.replaceAll(/\s\(/g,'('); + tmpWKT = tmpWKT.replaceAll(/\)\s/g,')'); + tmpWKT = tmpWKT.replaceAll(/\s\)/g,')'); + + return tmpWKT.toUpperCase(); + + }, + + /** + * + * @function + * @name _validatePointGeometry + * @description Validation d'un WKT de type POINT + * @param {string} geometry - Partie du WKT qui contient la géométrie (ici une paire de coordonnées est attendu) + * @return {boolean} + * + */ + + _validatePointGeometry: function(geometry) { + + let regex = new RegExp(/^-?\d+\.?\d*\s-?\d+\.?\d*\s?-?\d+\.?\d*$/); + return regex.test(geometry); + + }, + + /** + * + * @function + * @name _validateLinestringGeometry + * @description Validation d'un WKT de type LINESTRING + * @param {string} geometry - Partie du WKT qui contient la géométrie (ici plusieurs paires de coordonnées séparées par des ',' sont attendues) + * @return {boolean} + * + */ + + _validateLinestringGeometry: function(geometry) { + + let regex = new RegExp(/^(-?\d+\.?\d*\s-?\d+\.?\d*(\s-?\d+\.?\d*)?,?){2,}$/); + return regex.test(geometry); + + }, + + /** + * + * @function + * @name _validatePolygonGeometry + * @description Validation d'un WKT de type POLYGON + * @param {string} geometry - Partie du WKT qui contient la géométrie (ici plusieurs paires de coordonnées séparées par des ',' et regroupées dans des '()' sont attendues) + * @return {boolean} + * + */ + + _validatePolygonGeometry: function(geometry) { + + let regex = new RegExp(/^\((-?\d+\.?\d*\s-?\d+\.?\d*(\s-?\d+\.?\d*)?,?){3,}\)(,\((-?\d+\.?\d*\s-?\d+\.?\d*(\s-?\d+\.?\d*)?,?){3,}\))?$/); + return regex.test(geometry); + + }, + + /** + * + * @function + * @name _validateGeometryCollectionGeometry + * @description Validation d'un WKT de type GEOMETRYCOLLECTION + * @param {string} geometry - Partie du WKT qui contient la géométrie (ici plusieurs paires de coordonnées séparées par des ',' et regroupées dans des '()' sont attendues) + * @return {boolean} + * + */ + + _validateGeometryCollection: function(geometry) { + + let matches = geometry.matchAll(/([A-Z]+)/g); + + if (matches === null) { + return "La gemetrie ne peut etre analysee"; + } + + matches = Array.from(matches); + + for (let i = 0; i < matches.length; i++) { + + let curGeometry = ""; + if (i === matches.length-1) { + curGeometry = geometry.substring(matches[i]['index']+matches[i][1].length); + } else { + curGeometry = geometry.substring(matches[i]['index']+matches[i][1].length,matches[i+1]['index']-1); + } + curGeometry = curGeometry.substring(1,curGeometry.length-1); + + if (matches[i][1] === "POINT") { + if (!this._validatePointGeometry(curGeometry)) { + return "La gemetrie " + i + " point de la collection est invalide"; + } + } else if (matches[i][1] === "LINESTRING") { + if (!this._validateLinestringGeometry(curGeometry)) { + return "La gemetrie " + i + " linestring de la collection est invalide"; + } + } else if (matches[i][1] === "POLYGON") { + if (!this._validatePolygonGeometry(curGeometry)) { + return "La gemetrie " + i + " polygon de la collection est invalide"; + } + } else { + return "geometryType est inconnu: " + matches[i][1]; + } + + } + + return true; + + }, + + /** + * + * @function + * @name toGeoJSON + * @description Conversion d'un WKT en GeoJSON + * @param {string} wktString - WKT à convertir + * @return {object|string} JSON + * + */ + + toGeoJSON: function(wktString) { + + let finalGeoJSON = {}; + + let splitWkt = this.cleanWKT(wktString).match(/^(SRID:\d+;)?(POINT|LINESTRING|POLYGON|GEOMETRYCOLLECTION)\((.*)\)$/) + let geometryType = ""; + let geometry = ""; + if (splitWkt.length === 4) { + // il y a un SRID + geometryType = splitWkt[2]; + geometry = splitWkt[3]; + } else { + geometryType = splitWkt[1]; + geometry = splitWkt[2]; + } + + if (geometryType === "GEOMETRYCOLLECTION") { + finalGeoJSON = this._toGeometryCollection(geometry); + } else { + finalGeoJSON = this._toGeometry(geometryType, geometry); + } + + return finalGeoJSON; + + }, + + /** + * + * @function + * @name _toGeometry + * @description Conversion d'un WKT en géométrie de GeoJSON + * @param {string} geometryType - Type de la géométrie (selon le WKT d'origine) + * @param {string} geometry - Géométrie à convertir (selon le WKT d'origine) + * @return {object|string} JSON + * + */ + + _toGeometry: function(geometryType, geometry) { + + const mapping = { + POINT:"Point", + LINESTRING:"LineString", + POLYGON:"Polygon" + }; + + geometry = geometry.replaceAll(/(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)/g,"[$1,$2,$3]"); + geometry = geometry.replaceAll(/(-?\d+\.?\d*)\s(-?\d+\.?\d*)/g,"[$1,$2]"); + geometry = geometry.replaceAll(/\(/g,"["); + geometry = geometry.replaceAll(/\)/g,"]"); + + if (geometryType === "LINESTRING") { + geometry = "[" + geometry + "]"; + } + if (geometryType === "POLYGON") { + geometry = geometry.replaceAll(/\),\(/g,"],["); + geometry = "[" + geometry + "]"; + } + geometry = JSON.parse(geometry); + + return { + "type": mapping[geometryType], + "coordinates": geometry + }; + + }, + + /** + * + * @function + * @name _toGeometryCollection + * @description Conversion d'un WKT en GeoJSON + * @param {string} geometry - Géométrie à convertir (selon le WKT d'origine) + * @return {object|string} JSON + * + */ + + _toGeometryCollection: function(geometry) { + + let geometryCollection = { + "type": "GeometryCollection", + "geometries": new Array() + }; + + let matches = Array.from(geometry.matchAll(/([A-Z]+)/g)); + + for (let i = 0; i < matches.length; i++) { + + let curGeometry = ""; + if (i === matches.length-1) { + curGeometry = geometry.substring(matches[i]['index']+matches[i][1].length); + } else { + curGeometry = geometry.substring(matches[i]['index']+matches[i][1].length,matches[i+1]['index']-1); + } + curGeometry = curGeometry.substring(1,curGeometry.length-1); + + geometryCollection.geometries.push(this._toGeometry(matches[i][1], curGeometry)); + + } + + return geometryCollection; + + }, + + /** + * + * @function + * @name fromGeoJSON + * @description Conversion d'un GeoJSON en WKT + * @param {JSON} jsonObject - JSON à convertir + * @return {string} wktString + * + */ + + fromGeoJSON: function(jsonObject) { + + let finalWkt = ""; + + + + return finalWkt; + + } + + +} \ No newline at end of file diff --git a/test/unit/mocha/geometry/formats/testsWkt.js b/test/unit/mocha/geometry/formats/testsWkt.js new file mode 100644 index 0000000..6f3dc4b --- /dev/null +++ b/test/unit/mocha/geometry/formats/testsWkt.js @@ -0,0 +1,182 @@ +const assert = require('assert'); +const Wkt = require('../../../../../src/js/geometry/formats/wkt'); +const logManager = require('../../logManager'); + +const { hrtime } = require('node:process'); +const WKTmodule = require('wkt'); + +describe('Test de la classe Wkt', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let pointWkt = "POINT(2 48)"; + let linestringWkt = "LINESTRING(2 48,2.1 48.1,2.2 48.2)"; + let polygonWkt = "POLYGON((2 48,2.1 48.1,2.2 48.2,2 48))"; + let polygonWkt2 = "POLYGON((2 48,2.1 48.1,2.2 48.2,2 48),(2 48,2.1 48.1,2.2 48.2,2 48))"; + let geomcollWkt = "GEOMETRYCOLLECTION(POINT(2 48),LINESTRING(2 48,2.1 48.1,2.2 48.2),POLYGON((2 48,2.1 48.1,2.2 48.2,2 48),(2 48,2.1 48.1,2.2 48.2,2 48)))"; + let spaceWkt = " POLYGON ( (2 48, 2.1 48.1,2.2 48.2) , ( 2 48, 2.1 48.1 ,2.2 48.2 ) ) "; + let lowWkt = "polygon((2 48,2.1 48.1,2.2 48.2,2 48))"; + let eWkt = "SRID:4326;POINT(2 48)"; + let numberWkt = 1; + let objectWkt = {}; + let emptyWkt = ""; + let wrongTypeWkt = "TEST(2 48)"; + let noPointTypeWkt = "(2 48)"; + let noLinestringTypeWkt = "(2 48,2.1 48.1,2.2 48.2)"; + let wrongpointWkt = "POINT(2 48"; + let wrongpointWkt2 = "POINT(2)"; + let wronglineWkt = "LINESTRING(2 48"; + let wronglineWkt2 = "LINESTRING(2)"; + let wronglineWkt3 = "LINESTRING(2 48)"; + let wronglineWkt4 = "LINESTRING(2 48,2.1)"; + let wronglineWkt5 = "LINESTRING(2 48,"; + let wrongeWkt = "SID:4326;POINT(2 48)"; + let wrongeWkt2 = "SRID:;POINT(2 48)"; + let wrongeWkt3 = "SRID4326;POINT(2 48)"; + let loweWkt = "srid:4326;POINT(2 48)"; + let pointJson = {"type":"Point","coordinates":[2,48]}; + let linestringJson = {"type":"LineString","coordinates":[[2,48],[2.1,48.1],[2.2,48.2]]}; + let polygonJson = {"type":"Polygon","coordinates":[[[2,48],[2.1,48.1],[2.2,48.2],[2,48]]]}; + let polygonJson2 = {"type":"Polygon","coordinates":[[[2,48],[2.1,48.1],[2.2,48.2],[2,48]],[[2,48],[2.1,48.1],[2.2,48.2],[2,48]]]}; + let geometryCollectionJson = {"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[2,48]},{"type":"LineString","coordinates":[[2,48],[2.1,48.1],[2.2,48.2]]},{"type":"Polygon","coordinates":[[[2,48],[2.1,48.1],[2.2,48.2],[2,48]],[[2,48],[2.1,48.1],[2.2,48.2],[2,48]]]}]}; + + describe('Test de la fonction validateWKT()', function() { + + it('Avec un bon wkt (point)', function() { + assert.equal(Wkt.validateWKT(pointWkt), true); + }); + + it('Avec un bon wkt (linestring)', function() { + assert.equal(Wkt.validateWKT(linestringWkt), true); + }); + + it('Avec un bon wkt (polygon)', function() { + assert.equal(Wkt.validateWKT(polygonWkt), true); + }); + + it('Avec un bon wkt (polygon donut)', function() { + assert.equal(Wkt.validateWKT(polygonWkt2), true); + }); + + it('Avec un bon wkt (geometryCollection)', function() { + assert.equal(Wkt.validateWKT(geomcollWkt), true); + }); + + it('Avec un bon ewkt (point)', function() { + assert.equal(Wkt.validateWKT(eWkt), true); + }); + + it('Avec un wkt qui contient des espaces', function() { + assert.equal(Wkt.validateWKT(spaceWkt), true); + }); + + it('Avec un wkt (insensitive case)', function() { + assert.equal(Wkt.validateWKT(lowWkt), true); + }); + + it('Avec un ewkt (insensitive case)', function() { + assert.equal(Wkt.validateWKT(loweWkt), true); + }); + + it('Avec un nombre', function() { + assert.equal(Wkt.validateWKT(numberWkt), "L'argument n'est pas une chaîne"); + }); + + it('Avec un objet vide', function() { + assert.equal(Wkt.validateWKT(objectWkt), "L'argument n'est pas une chaîne"); + }); + + it('Avec une chaine vide', function() { + assert.equal(Wkt.validateWKT(emptyWkt), "L'argument est vide"); + }); + + it('Avec rien', function() { + assert.equal(Wkt.validateWKT(), "L'argument est vide"); + }); + + it('Avec un mauvais type', function() { + assert.equal(Wkt.validateWKT(wrongTypeWkt), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Linestring sans type', function() { + assert.equal(Wkt.validateWKT(noPointTypeWkt), "La chaine est trop courte"); + }); + + it('Linestring sans type', function() { + assert.equal(Wkt.validateWKT(noLinestringTypeWkt), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Mauvais wkt (point)', function() { + assert.equal(Wkt.validateWKT(wrongpointWkt), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Mauvais wkt (point) 2', function() { + assert.equal(Wkt.validateWKT(wrongpointWkt2), "La chaine est trop courte"); + }); + + it('Mauvais wkt (linestring)', function() { + assert.equal(Wkt.validateWKT(wronglineWkt), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Mauvais wkt (linestring) 2', function() { + assert.equal(Wkt.validateWKT(wronglineWkt2), "La validation a echoue"); + }); + + it('Mauvais wkt (linestring) 3', function() { + assert.equal(Wkt.validateWKT(wronglineWkt3), "La validation a echoue"); + }); + + it('Mauvais wkt (linestring) 4', function() { + assert.equal(Wkt.validateWKT(wronglineWkt4), "La validation a echoue"); + }); + + it('Mauvais wkt (linestring) 5', function() { + assert.equal(Wkt.validateWKT(wronglineWkt5), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Mauvais ewkt (point)', function() { + assert.equal(Wkt.validateWKT(wrongeWkt), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Mauvais ewkt (point) 2', function() { + assert.equal(Wkt.validateWKT(wrongeWkt2), "La chaine nettoyee ne peut etre splittee"); + }); + + it('Mauvais ewkt (point) 3', function() { + assert.equal(Wkt.validateWKT(wrongeWkt3), "La chaine nettoyee ne peut etre splittee"); + }); + + }); + + describe('Test de la fonction toGeoJSON()', function() { + + it('Avec un bon wkt (point)', function() { + assert.deepStrictEqual(Wkt.toGeoJSON(pointWkt), pointJson); + }); + + it('Avec un bon wkt (linestring)', function() { + assert.deepStrictEqual(Wkt.toGeoJSON(linestringWkt), linestringJson); + }); + + it('Avec un bon wkt (polygon simple)', function() { + assert.deepStrictEqual(Wkt.toGeoJSON(polygonWkt), polygonJson); + }); + + it('Avec un bon wkt (polygon donuts)', function() { + assert.deepStrictEqual(Wkt.toGeoJSON(polygonWkt2), polygonJson2); + }); + + it('Avec un bon wkt (geometry collection)', function() { + assert.deepStrictEqual(Wkt.toGeoJSON(geomcollWkt), geometryCollectionJson); + }); + + it('Avec un bon ewkt (point)', function() { + assert.deepStrictEqual(Wkt.toGeoJSON(eWkt), pointJson); + }); + + }); + +}); From 5fafc0d6555320c79c67ff9650b685f24e7be9e3 Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 15 Dec 2022 11:48:08 +0100 Subject: [PATCH 33/93] [test] maj des tests unitaires du projectionManager --- docker/dev/docker-compose.yml | 2 +- .../mocha/geography/testsProjectionManager.js | 82 +++++++++---------- test/unit/mocha/topology/testsTopology.js | 47 ----------- test/unit/mocha/utils/testsStorageManager.js | 40 --------- 4 files changed, 42 insertions(+), 129 deletions(-) delete mode 100644 test/unit/mocha/topology/testsTopology.js delete mode 100644 test/unit/mocha/utils/testsStorageManager.js diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index a7869ec..9c4c2fa 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -11,7 +11,7 @@ services: - pgrouting environment: - NODE_ENV=debug - command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" + command : "sleep 60000" ports: - 8080:8080 - 9229:9229 diff --git a/test/unit/mocha/geography/testsProjectionManager.js b/test/unit/mocha/geography/testsProjectionManager.js index 1173859..1f9ad4f 100644 --- a/test/unit/mocha/geography/testsProjectionManager.js +++ b/test/unit/mocha/geography/testsProjectionManager.js @@ -9,7 +9,7 @@ describe('Test de la classe ProjectionManager', function() { logManager.manageLogs(); }); - describe('Test du constructeur et des getters/setters', function() { + describe('Test du constructeur', function() { let projManager = new ProjectionManager(); @@ -33,44 +33,44 @@ describe('Test de la classe ProjectionManager', function() { }); - describe('Vérification d\'un fichier de projections', function() { + describe('Test de isProjectionChecked', function() { let projManager = new ProjectionManager(); - let file = "/home/docker/app/test/unit/mocha/config/projections/projection.json"; + let directory = "/home/docker/app/test/unit/mocha/config/projections/"; - it('checkProjectionFile()', function() { - assert.equal(projManager.checkProjectionFile(file), true); - assert.equal(projManager.isChecked("EPSG:4326"), true); - assert.equal(projManager.isChecked("EPSG:2154"), true); - assert.equal(projManager.isChecked("EPSG:2155"), false); + it('isProjectionChecked()', function() { + projManager.checkProjectionDirectory(directory); + assert.equal(projManager.isProjectionChecked("EPSG:4326"), true); + assert.equal(projManager.isProjectionChecked("EPSG:2154"), true); + assert.equal(projManager.isProjectionChecked("EPSG:2155"), false); }); }); - describe('Vérification d\'un dossier de projections', function() { + describe('Vérification d\'un fichier de projections', function() { let projManager = new ProjectionManager(); - let directory = "/home/docker/app/test/unit/mocha/config/projections/"; + let file = "/home/docker/app/test/unit/mocha/config/projections/projection.json"; - it('checkProjectionDirectory()', function() { - assert.equal(projManager.checkProjectionDirectory(directory), true); - assert.equal(projManager.isChecked("EPSG:4326"), true); - assert.equal(projManager.isChecked("EPSG:2154"), true); - assert.equal(projManager.isChecked("EPSG:2155"), false); + it('checkProjectionFile()', function() { + assert.equal(projManager.checkProjectionFile(file), true); + assert.equal(projManager.isProjectionChecked("EPSG:4326"), true); + assert.equal(projManager.isProjectionChecked("EPSG:2154"), true); + assert.equal(projManager.isProjectionChecked("EPSG:2155"), false); }); }); - describe('Test de isChecked', function() { + describe('Vérification d\'un dossier de projections', function() { let projManager = new ProjectionManager(); let directory = "/home/docker/app/test/unit/mocha/config/projections/"; - it('isChecked()', function() { - projManager.checkProjectionDirectory(directory); - assert.equal(projManager.isChecked("EPSG:4326"), true); - assert.equal(projManager.isChecked("EPSG:2154"), true); - assert.equal(projManager.isChecked("EPSG:2155"), false); + it('checkProjectionDirectory()', function() { + assert.equal(projManager.checkProjectionDirectory(directory), true); + assert.equal(projManager.isProjectionChecked("EPSG:4326"), true); + assert.equal(projManager.isProjectionChecked("EPSG:2154"), true); + assert.equal(projManager.isProjectionChecked("EPSG:2155"), false); }); }); @@ -90,44 +90,44 @@ describe('Test de la classe ProjectionManager', function() { }); - describe('Chargement d\'un fichier de projections', function() { + describe('Test de isProjectionLoaded', function() { let projManager = new ProjectionManager(); - let file = "/home/docker/app/test/unit/mocha/config/projections/projection.json"; + let directory = "/home/docker/app/test/unit/mocha/config/projections/"; - it('loadProjectionFile()', function() { - assert.equal(projManager.loadProjectionFile(file), true); - assert.equal(projManager.isAvailable("EPSG:4326"), true); - assert.equal(projManager.isAvailable("EPSG:2154"), true); - assert.equal(projManager.isAvailable("EPSG:2155"), false); + it('isProjectionLoaded()', function() { + projManager.loadProjectionDirectory(directory); + assert.equal(projManager.isProjectionLoaded("EPSG:4326"), true); + assert.equal(projManager.isProjectionLoaded("EPSG:2154"), true); + assert.equal(projManager.isProjectionLoaded("EPSG:2155"), false); }); }); - describe('Chargement d\'un dossier de projections', function() { + describe('Chargement d\'un fichier de projections', function() { let projManager = new ProjectionManager(); - let directory = "/home/docker/app/test/unit/mocha/config/projections/"; + let file = "/home/docker/app/test/unit/mocha/config/projections/projection.json"; - it('loadProjectionDirectory()', function() { - assert.equal(projManager.loadProjectionDirectory(directory), true); - assert.equal(projManager.isAvailable("EPSG:4326"), true); - assert.equal(projManager.isAvailable("EPSG:2154"), true); - assert.equal(projManager.isAvailable("EPSG:2155"), false); + it('loadProjectionFile()', function() { + assert.equal(projManager.loadProjectionFile(file), true); + assert.equal(projManager.isProjectionLoaded("EPSG:4326"), true); + assert.equal(projManager.isProjectionLoaded("EPSG:2154"), true); + assert.equal(projManager.isProjectionLoaded("EPSG:2155"), false); }); }); - describe('Test de isAvailable', function() { + describe('Chargement d\'un dossier de projections', function() { let projManager = new ProjectionManager(); let directory = "/home/docker/app/test/unit/mocha/config/projections/"; - it('isAvailable()', function() { - projManager.loadProjectionDirectory(directory); - assert.equal(projManager.isAvailable("EPSG:4326"), true); - assert.equal(projManager.isAvailable("EPSG:2154"), true); - assert.equal(projManager.isAvailable("EPSG:2155"), false); + it('loadProjectionDirectory()', function() { + assert.equal(projManager.loadProjectionDirectory(directory), true); + assert.equal(projManager.isProjectionLoaded("EPSG:4326"), true); + assert.equal(projManager.isProjectionLoaded("EPSG:2154"), true); + assert.equal(projManager.isProjectionLoaded("EPSG:2155"), false); }); }); diff --git a/test/unit/mocha/topology/testsTopology.js b/test/unit/mocha/topology/testsTopology.js deleted file mode 100644 index e877eca..0000000 --- a/test/unit/mocha/topology/testsTopology.js +++ /dev/null @@ -1,47 +0,0 @@ -const assert = require('assert'); -const Topology = require('../../../../src/js/topology/topology'); -const logManager = require('../logManager'); - -describe('Test de la classe Topology', function() { - - before(function() { - // runs before all tests in this block - logManager.manageLogs(); - }); - - let configuration = { - "id": "corse-osm", - "type": "osm", - "description": "Corse en version OSM", - "projection": "EPSG:4326", - "bbox": "-180,-90,180,90" - }; - - - describe('Test du constructeur et des getters', function() { - - let topology = new Topology(configuration.id, configuration.type, configuration.description, configuration.projection, configuration.bbox); - - it('Get Id', function() { - assert.equal(topology.id, configuration.id); - }); - - it('Get Type', function() { - assert.equal(topology.type, configuration.type); - }); - - it('Get Description', function() { - assert.equal(topology.description, configuration.description); - }); - - it('Get Projection', function() { - assert.equal(topology.projection, configuration.projection); - }); - - it('Get Bbox', function() { - assert.equal(topology.bbox, configuration.bbox); - }); - - }); - -}); diff --git a/test/unit/mocha/utils/testsStorageManager.js b/test/unit/mocha/utils/testsStorageManager.js deleted file mode 100644 index 9db60b0..0000000 --- a/test/unit/mocha/utils/testsStorageManager.js +++ /dev/null @@ -1,40 +0,0 @@ -const assert = require('assert'); -const storageManager = require('../../../../src/js/utils/storageManager'); -const logManager = require('../logManager'); - -describe('Test du storageManager', function() { - - before(function() { - // runs before all tests in this block - logManager.manageLogs(); - }); - - describe('Test de la fonction checkJsonStorage()', function() { - - let json = { - "file": "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" - }; - - let jsonNoFile = { - "file": "/home/docker/data/corse-latest.osrm" - }; - - let wrongJson = { - "fil": "/home/docker/data/corse-latest.osrm" - }; - - it('checkJsonStorage() should return true when all is correct', function() { - assert.equal(storageManager.checkJsonStorage(json), true); - }); - - it('checkJsonStorage() should return false when no file', function() { - assert.equal(storageManager.checkJsonStorage(jsonNoFile), false); - }); - - it('checkJsonStorage() should return false when the json is incorrect', function() { - assert.equal(storageManager.checkJsonStorage(wrongJson), false); - }); - - }); - -}); From ebed8ae6a6f87d727e7899e417fbb13e08755b26 Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 16 Dec 2022 15:50:18 +0100 Subject: [PATCH 34/93] [feat] conversion geojson vers wkt dans le module --- src/js/geometry/formats/wkt.js | 66 ++++++++++++++++++-- test/unit/mocha/geometry/formats/testsWkt.js | 24 +++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/js/geometry/formats/wkt.js b/src/js/geometry/formats/wkt.js index 6d01404..04d8799 100644 --- a/src/js/geometry/formats/wkt.js +++ b/src/js/geometry/formats/wkt.js @@ -326,14 +326,72 @@ module.exports = { * */ - fromGeoJSON: function(jsonObject) { + fromGeoJSON: function(jsonObject) { - let finalWkt = ""; + if (jsonObject.type === "Point") { - + return this._fromPoint(jsonObject); + + } else if (jsonObject.type === "LineString") { + + return this._internalFrom(jsonObject,"LINESTRING"); + + } else if (jsonObject.type === "Polygon") { + + return this._internalFrom(jsonObject,"POLYGON"); - return finalWkt; + } else if (jsonObject.type === "GeometryCollection") { + + let arrayWkt = new Array(); + for(let geometry of jsonObject.geometries) { + arrayWkt.push(this.fromGeoJSON(geometry)); + } + return "GEOMETRYCOLLECTION(" + arrayWkt.toString() + ")"; + + } else { + return ""; + } + }, + + /** + * + * @function + * @name _fromPoint + * @description Conversion d'un GeoJSON en WKT + * @param {JSON} jsonObject - JSON à convertir + * @return {string} wktString + * + */ + + _fromPoint: function(jsonObject) { + + let geometry = JSON.stringify(jsonObject.coordinates); + geometry = geometry.replaceAll(/\[(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*)\]/g,"($1 $2 $3)"); + geometry = geometry.replaceAll(/\[(-?\d+\.?\d*),(-?\d+\.?\d*)\]/g,"($1 $2)"); + return "POINT"+geometry; + + }, + + /** + * + * @function + * @name _internalFrom + * @description Conversion d'un GeoJSON en WKT + * @param {JSON} jsonObject - JSON à convertir + * @return {string} wktString + * + */ + + _internalFrom: function(jsonObject, type) { + + let geometry = JSON.stringify(jsonObject.coordinates); + geometry = geometry.replaceAll(/\[(-?\d+\.?\d*),(-?\d+\.?\d*),(-?\d+\.?\d*)\]/g,"$1 $2 $3"); + geometry = geometry.replaceAll(/\[(-?\d+\.?\d*),(-?\d+\.?\d*)\]/g,"$1 $2"); + geometry = geometry.replaceAll(/\[/g,"("); + geometry = geometry.replaceAll(/\]/g,")"); + return type+geometry; + } diff --git a/test/unit/mocha/geometry/formats/testsWkt.js b/test/unit/mocha/geometry/formats/testsWkt.js index 6f3dc4b..0881545 100644 --- a/test/unit/mocha/geometry/formats/testsWkt.js +++ b/test/unit/mocha/geometry/formats/testsWkt.js @@ -179,4 +179,28 @@ describe('Test de la classe Wkt', function() { }); + describe('Test de la fonction fromGeoJSON()', function() { + + it('Avec un bon geojson (point)', function() { + assert.deepStrictEqual(Wkt.fromGeoJSON(pointJson), pointWkt); + }); + + it('Avec un bon geojson (linestring)', function() { + assert.deepStrictEqual(Wkt.fromGeoJSON(linestringJson), linestringWkt); + }); + + it('Avec un bon geojson (polygon)', function() { + assert.deepStrictEqual(Wkt.fromGeoJSON(polygonJson), polygonWkt); + }); + + it('Avec un bon geojson (polygon donuts)', function() { + assert.deepStrictEqual(Wkt.fromGeoJSON(polygonJson2), polygonWkt2); + }); + + it('Avec un bon geojson (geometry collection)', function() { + assert.deepStrictEqual(Wkt.fromGeoJSON(geometryCollectionJson), geomcollWkt); + }); + + }); + }); From 42de23eccd6053d57ed49d81ee8b589277f59671 Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 16 Dec 2022 17:31:08 +0100 Subject: [PATCH 35/93] [deps] remplacement du module wkt par la lib interne --- changelog.md | 1 + package.json | 4 +--- src/js/sources/smartroutingSource.js | 6 +++--- test/integration/readme.md | 4 ---- test/unit/mocha/geometry/formats/testsWkt.js | 3 --- test/unit/readme.md | 3 ++- 6 files changed, 7 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index accf6ff..d2482c9 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ ADDED: - La classe Administrator permet de gérer le service via une API. Notamment la création, la suppression et la modification d'un service seront possible. - Cette classe est configurée par un nouveau fichier de configuration. - Ajout du moteur Valhalla pour les itinéraires et les isochrones + - Le module `wkt` a été remplacé par une implémentation interne CHANGED: - L'option --configCheck au démarrage de Road2 n'a plus exactement le même comportement. diff --git a/package.json b/package.json index cc8710b..4ccaf25 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,7 @@ "https-proxy-agent": "5.0.1", "log4js": "6.7.1", "nconf": "0.12.0", - "proj4": "2.8.0", - "wkt": "0.1.1" + "proj4": "2.8.0" }, "optionalDependencies": { "osrm": "5.26.0", @@ -53,7 +52,6 @@ "proj4", "assert", "helmet", - "wkt", "got", "http-proxy-agent" ] diff --git a/src/js/sources/smartroutingSource.js b/src/js/sources/smartroutingSource.js index 0d179d0..be53d34 100644 --- a/src/js/sources/smartroutingSource.js +++ b/src/js/sources/smartroutingSource.js @@ -13,7 +13,7 @@ const Distance = require('../geography/distance'); const Duration = require('../time/duration'); const errorManager = require('../utils/errorManager'); const log4js = require('log4js'); -const wkt = require('wkt'); +const wkt = require('../geometry/formats/wkt'); const httpQuery = require('../utils/httpQuery'); @@ -330,7 +330,7 @@ module.exports = class smartroutingSource extends Source { // --- // convertion de la geometry - const wayGeojson = wkt.parse(smartroutingResponse.geometryWkt); + const wayGeojson = wkt.toGeoJSON(smartroutingResponse.geometryWkt); let way = new Line(wayGeojson, 'geojson', askedProjection); // start et end @@ -443,7 +443,7 @@ module.exports = class smartroutingSource extends Source { location = new Point(locationCoords[0], locationCoords[1], projection); // Geometrie - const rawGeometry = wkt.parse(smartroutingResponse.wktGeometry); + const rawGeometry = wkt.toGeoJSON(smartroutingResponse.wktGeometry); // Cas où il n'y a pas d'isochrone car costValue trop faible if (rawGeometry === null) { diff --git a/test/integration/readme.md b/test/integration/readme.md index a733d48..3bc24f3 100644 --- a/test/integration/readme.md +++ b/test/integration/readme.md @@ -145,10 +145,6 @@ Autres: - defs() - () -- wkt - - sources/smartroutingSource.js - - parse() - - osrm - sources/sourceManager.js - sources/osrmSource.js diff --git a/test/unit/mocha/geometry/formats/testsWkt.js b/test/unit/mocha/geometry/formats/testsWkt.js index 0881545..4b96eaa 100644 --- a/test/unit/mocha/geometry/formats/testsWkt.js +++ b/test/unit/mocha/geometry/formats/testsWkt.js @@ -2,9 +2,6 @@ const assert = require('assert'); const Wkt = require('../../../../../src/js/geometry/formats/wkt'); const logManager = require('../../logManager'); -const { hrtime } = require('node:process'); -const WKTmodule = require('wkt'); - describe('Test de la classe Wkt', function() { before(function() { diff --git a/test/unit/readme.md b/test/unit/readme.md index cf18680..9cd4908 100644 --- a/test/unit/readme.md +++ b/test/unit/readme.md @@ -32,4 +32,5 @@ On trouvera donc les classes ou les fichiers suivants: - processManager (log4js) - simplify.js //TODO - storageManager (log4js) -- validationManager \ No newline at end of file +- validationManager +- wkt \ No newline at end of file From b152309466f20b84701414a007450c023b75e128 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 19 Dec 2022 13:42:48 +0100 Subject: [PATCH 36/93] =?UTF-8?q?[feat]=20gestion=20du=20wkt=20dans=20les?= =?UTF-8?q?=20r=C3=A9ponses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/apis/simple/1.0.0/api.yaml | 8 +- .../configuration/resources/osrm.resource | 3 +- .../configuration/resources/pgr.resource | 6 +- .../configuration/resources/smartpgr.resource | 6 +- .../configuration/resources/valhalla.resource | 6 +- documentation/developers/functionnalities.md | 4 +- src/js/geometry/line.js | 51 +++++-------- src/js/geometry/point.js | 10 +-- src/js/geometry/polygon.js | 76 +++++++++++++------ .../features/req-simple-1.0.0-osrm.feature | 16 ++++ .../features/req-simple-1.0.0-pgr.feature | 32 ++++++++ .../req-simple-1.0.0-smartrouting.feature | 18 ++++- .../req-simple-1.0.0-valhalla.feature | 34 +++++++++ .../mocha/geometry/integrationLine.js | 8 -- 14 files changed, 194 insertions(+), 84 deletions(-) diff --git a/documentation/apis/simple/1.0.0/api.yaml b/documentation/apis/simple/1.0.0/api.yaml index 24fb6fa..6f115c6 100644 --- a/documentation/apis/simple/1.0.0/api.yaml +++ b/documentation/apis/simple/1.0.0/api.yaml @@ -92,7 +92,7 @@ paths: required: false schema: type: "string" - enum: ["geojson","polyline"] + enum: ["geojson","polyline","wkt"] - name: "constraints" in: "query" description: "Contraintes utilisées pour le calcul. Il s'agit d'un objet JSON (voir la version POST de cette opération). Les paramètres disponibles sont présents dans le GetCapabilities." @@ -377,7 +377,7 @@ paths: required: false schema: type: "string" - enum: ["geojson","polyline"] + enum: ["geojson","polyline","wkt"] - name: "distanceUnit" in: "query" description: "Unité des distances renvoyées. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities." @@ -526,7 +526,7 @@ components: type: "boolean" geometryFormat: type: "string" - enum: ["geojson","polyline"] + enum: ["geojson","polyline","wkt"] getBbox: type: "boolean" distanceUnit: @@ -574,7 +574,7 @@ components: $ref: "#/components/schemas/constraintIso" geometryFormat: type: "string" - enum: ["geojson","polyline"] + enum: ["geojson","polyline","wkt"] distanceUnit: type: "string" timeUnit: diff --git a/documentation/configuration/resources/osrm.resource b/documentation/configuration/resources/osrm.resource index 7620497..52987c5 100644 --- a/documentation/configuration/resources/osrm.resource +++ b/documentation/configuration/resources/osrm.resource @@ -67,7 +67,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { diff --git a/documentation/configuration/resources/pgr.resource b/documentation/configuration/resources/pgr.resource index 71d25ab..67b2244 100644 --- a/documentation/configuration/resources/pgr.resource +++ b/documentation/configuration/resources/pgr.resource @@ -99,7 +99,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { @@ -413,7 +414,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { diff --git a/documentation/configuration/resources/smartpgr.resource b/documentation/configuration/resources/smartpgr.resource index 5d610ed..d0b3136 100644 --- a/documentation/configuration/resources/smartpgr.resource +++ b/documentation/configuration/resources/smartpgr.resource @@ -101,7 +101,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { @@ -414,7 +415,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { diff --git a/documentation/configuration/resources/valhalla.resource b/documentation/configuration/resources/valhalla.resource index ee66b77..2f8d8ee 100644 --- a/documentation/configuration/resources/valhalla.resource +++ b/documentation/configuration/resources/valhalla.resource @@ -67,7 +67,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { @@ -203,7 +204,8 @@ "defaultValueContent": "geojson", "values": [ "geojson", - "polyline" + "polyline", + "wkt" ] }, { diff --git a/documentation/developers/functionnalities.md b/documentation/developers/functionnalities.md index 7ea56ee..14dfe57 100644 --- a/documentation/developers/functionnalities.md +++ b/documentation/developers/functionnalities.md @@ -34,7 +34,7 @@ Selon les données présentes dans les graphes, il est possible de choisir les i Par l'intermédiaire de plusieurs paramètres, il est possible de préciser le contenu de la réponse : - La présence ou non des étapes du parcours. -- Le format des géométries dans la réponse. Pour le moment, geojson et polyline sont disponibles. +- Le format des géométries dans la réponse. Pour le moment, geojson, polyline et wkt sont disponibles. - La présence ou non d'une bbox dans la réponse. ### 1.8 : Choisir les unités de la requête et de la réponse @@ -80,7 +80,7 @@ La notion de contrainte est définie dans les [concepts](./concepts.md). Elle es ### 2.8 : Préciser le contenu de la réponse Par l'intermédiaire de plusieurs paramètres, un seul pour le moment, il est possible de préciser le contenu de la réponse : -- Le format des géométrie dans la réponse. Pour le moment, geojson et polyline sont disponibles. +- Le format des géométrie dans la réponse. Pour le moment, geojson, polyline et wkt sont disponibles. ### 2.9 : Choisir les unités de la requête et de la réponse diff --git a/src/js/geometry/line.js b/src/js/geometry/line.js index a2076dd..e03f4e2 100644 --- a/src/js/geometry/line.js +++ b/src/js/geometry/line.js @@ -2,7 +2,8 @@ const errorManager = require('../utils/errorManager'); const polyline = require('@mapbox/polyline'); -const Geometry = require('../geometry/geometry'); +const wkt = require('./formats/wkt'); +const Geometry = require('./geometry'); const proj4 = require('proj4'); const assert = require('assert'); @@ -30,7 +31,7 @@ module.exports = class Line extends Geometry { */ constructor(geom, format, projection, polylinePrecision = 5) { - super("polyline", projection); + super("line", projection); // Géométrie de la polyline this._geom = geom; @@ -38,9 +39,10 @@ module.exports = class Line extends Geometry { // Format de géométrie (geojson, polyline...) this._format = format; - if (polylinePrecision != 5) { + if (polylinePrecision != 5 && this._format === "polyline") { this._geom = polyline.encode(polyline.decode(geom, polylinePrecision)); } + } /** @@ -54,29 +56,6 @@ module.exports = class Line extends Geometry { return this._geom; } - /** - * - * @function - * @name getGeoJSON - * @description Récupérer la représentation geoJSON de la ligne - * - */ - getGeoJSON () { - return this._convertGeometry(this._geom, this._format, 'geojson'); - } - - /** - * - * @function - * @name getEncodedPolyline - * @description Récupérer la représentation polyline de la ligne - * - */ - getEncodedPolyline () { - return this._convertGeometry(this._geom, this._format, 'polyline'); - } - - /** * * @function @@ -94,8 +73,8 @@ module.exports = class Line extends Geometry { * @name convertGeometry * @description Convertit une géométrie depuis un format vers un autre * @param {Object|string} geom - Géométrie source - * @param {string} srcFormat - type de la gémétrie source pour l'instant, dans {geojson, polyline} - * @param {string} outFormat - type voulu en sortie pour l'instant, dans {geojson, polyline} + * @param {string} srcFormat - type de la gémétrie source pour l'instant, dans {geojson, polyline,wkt} + * @param {string} outFormat - type voulu en sortie pour l'instant, dans {geojson, polyline,wkt} * @return {Object|string} out_geom - géométrie convertie * */ @@ -106,6 +85,14 @@ module.exports = class Line extends Geometry { return polyline.toGeoJSON(geom); } else if (srcFormat === "geojson" && outFormat === "polyline") { return polyline.fromGeoJSON(geom); + } else if (srcFormat === "wkt" && outFormat === "geojson") { + return wkt.toGeoJSON(geom); + } else if (srcFormat === "geojson" && outFormat === "wkt") { + return wkt.fromGeoJSON(geom); + } else if (srcFormat === "polyline" && outFormat === "wkt") { + return wkt.fromGeoJSON(polyline.toGeoJSON(geom)); + } else if (srcFormat === "wkt" && outFormat === "polyline") { + return polyline.fromGeoJSON(wkt.toGeoJSON(geom)); } else { //TODO: voir si on peut remplacer ce throw par un return {} throw errorManager.createError("Unsupported geometry conversion"); @@ -122,7 +109,7 @@ module.exports = class Line extends Geometry { */ transform (projection) { - if (this.projection !== projection) { + if (super.projection !== projection) { let tmpGeom = this.getLineIn(projection, this._format); @@ -134,7 +121,7 @@ module.exports = class Line extends Geometry { } catch (err) { this._geom = tmpGeom; - this.projection = projection; + super.projection = projection; return true; @@ -158,7 +145,7 @@ module.exports = class Line extends Geometry { */ getLineIn (projection, format) { - let geojson = this.getGeoJSON(); + let geojson = this._convertGeometry(this._geom, this._format, 'geojson'); // vérifications sur le geojson à reprojeter if (!geojson.coordinates) { @@ -176,7 +163,7 @@ module.exports = class Line extends Geometry { for (let i = 0; i < geojson.coordinates.length; i++) { - let reprojectedPoint = proj4(this.projection, projection, [geojson.coordinates[i][0], geojson.coordinates[i][1]]); + let reprojectedPoint = proj4(super.projection, projection, [geojson.coordinates[i][0], geojson.coordinates[i][1]]); if (!Array.isArray(reprojectedPoint)) { return {}; diff --git a/src/js/geometry/point.js b/src/js/geometry/point.js index a06078f..28a0818 100644 --- a/src/js/geometry/point.js +++ b/src/js/geometry/point.js @@ -68,8 +68,8 @@ module.exports = class Point extends Geometry { */ getCoordinatesIn (projection) { - if (this.projection !== projection) { - return proj4(this.projection, projection, [this._x, this._y]); + if (super.projection !== projection) { + return proj4(super.projection, projection, [this._x, this._y]); } else { return [this._x, this._y]; } @@ -86,9 +86,9 @@ module.exports = class Point extends Geometry { */ transform (projection) { - if (this.projection !== projection) { + if (super.projection !== projection) { - let reprojectedPoint = proj4(this.projection, projection, [this._x, this._y]); + let reprojectedPoint = proj4(super.projection, projection, [this._x, this._y]); if (!Array.isArray(reprojectedPoint)) { return false; @@ -99,7 +99,7 @@ module.exports = class Point extends Geometry { this._x = reprojectedPoint[0]; this._y = reprojectedPoint[1]; - this.projection = projection; + super.projection = projection; return true; diff --git a/src/js/geometry/polygon.js b/src/js/geometry/polygon.js index 2c95bbd..a4b1f6e 100644 --- a/src/js/geometry/polygon.js +++ b/src/js/geometry/polygon.js @@ -3,6 +3,7 @@ const errorManager = require('../utils/errorManager'); const turf = require('@turf/turf'); const polyline = require('@mapbox/polyline'); +const wkt = require('./formats/wkt'); const Geometry = require('../geometry/geometry'); const proj4 = require('proj4'); @@ -43,17 +44,6 @@ module.exports = class Polygon extends Geometry { return this._geom; } - /** - * - * @function - * @name getGeoJSON - * @description Récupérer la représentation geoJSON du polygon - * - */ - getGeoJSON () { - return this._convertGeometry(this._geom, this._format, 'geojson'); - } - /** * * @function @@ -81,12 +71,54 @@ module.exports = class Polygon extends Geometry { if (srcFormat === outFormat) { return geom; } else if (srcFormat === "polyline" && outFormat === "geojson") { - let tmpPolygon = polyline.toGeoJSON(geom); - tmpPolygon.type = "Polygon"; - return tmpPolygon; + return this._polyline2GeoJson(geom); } else if (srcFormat === "geojson" && outFormat === "polyline") { + return this._geoJson2Polyline(geom); + } else if (srcFormat === "wkt" && outFormat === "geojson") { + return wkt.toGeoJSON(geom); + } else if (srcFormat === "geojson" && outFormat === "wkt") { + return wkt.fromGeoJSON(geom); + } else if (srcFormat === "polyline" && outFormat === "wkt") { + return wkt.fromGeoJSON(this.__polyline2GeoJson(geom)); + } else if (srcFormat === "wkt" && outFormat === "polyline") { + return this._geoJson2Polyline(wkt.toGeoJSON(geom)); + } else { + //TODO: voir si on peut remplacer ce throw par un return {} + throw errorManager.createError("Unsupported geometry conversion"); + } + + } - let result = []; + /** + * + * @function + * @name _polyline2GeoJson + * @description Convertit une géométrie GeoJSON vers Polyline + * @param {Object|string} geom - Géométrie source + * @return {Object|string} out_geom - géométrie convertie + * + */ + _polyline2GeoJson (geom) { + + let tmpGeoJSON = polyline.toGeoJSON(geom); + tmpGeoJSON.type = "Polygon"; + return tmpGeoJSON; + + } + + + /** + * + * @function + * @name _geoJson2Polyline + * @description Convertit une géométrie GeoJSON vers Polyline + * @param {Object|string} geom - Géométrie source + * @return {Object|string} out_geom - géométrie convertie + * + */ + _geoJson2Polyline (geom) { + + let result = []; if (geom.type === "Point") { // Cas où l'isochrone est un simple point @@ -119,11 +151,6 @@ module.exports = class Polygon extends Geometry { } - } else { - //TODO: voir si on peut remplacer ce throw par un return {} - throw errorManager.createError("Unsupported geometry conversion"); - } - } @@ -141,7 +168,7 @@ module.exports = class Polygon extends Geometry { return true; } - let geojson = this.getGeoJSON(); + let geojson = this._convertGeometry(this._geom, this._format, 'geojson'); // vérifications sur le geojson à reprojeter if (!geojson.coordinates) { @@ -187,7 +214,7 @@ module.exports = class Polygon extends Geometry { for (let c = 0; c < geojson.coordinates[g].length; c++) { - let reprojectedPoint = proj4(this.projection, projection, [geojson.coordinates[g][c][0], geojson.coordinates[g][c][1]]); + let reprojectedPoint = proj4(super.projection, projection, [geojson.coordinates[g][c][0], geojson.coordinates[g][c][1]]); if (!Array.isArray(reprojectedPoint)) { return false; @@ -203,9 +230,8 @@ module.exports = class Polygon extends Geometry { } - // TODO: gérer les différents formats de géométrie - - this._projection = projection; + this._geom = this._convertGeometry(geojson, 'geojson', this._format); + super.projection = projection; return true; diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature index 225bdbb..8bb2a5a 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-osrm.feature @@ -599,6 +599,22 @@ Scenario Outline: [] Route sur l'API simple 1.0.0 avec geometryFormat=po | GET | | POST | + Scenario Outline: [] Route sur l'API simple 1.0.0 avec geometryFormat=wkt + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-osrm" + And with query parameters: + | key | value | + | geometryFormat | wkt | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain a string attribute "geometry" + Examples: + | method | + | GET | + | POST | + Scenario Outline: [] Route sur l'API simple 1.0.0 avec mauvais geometryFormat Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-osrm" diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index 3a511fa..8aa32a5 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -50,6 +50,22 @@ Feature: Road2-PGR | GET | | POST | + Scenario Outline: [] Route sur l'API simple 1.0.0 avec geometryFormat=wkt + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | geometryFormat | wkt | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain a string attribute "geometry" + Examples: + | method | + | GET | + | POST | + Scenario: [GET] Route sur l'API simple 1.0.0 avec deux contraintes sur une ressource pgr Given an "GET" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-pgr" @@ -784,6 +800,22 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 | GET | | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec geometryFormat=wkt + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone" + And with query parameters: + | key | value | + | geometryFormat | wkt | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the response should contain a string attribute "geometry" + Examples: + | method | + | GET | + | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec un mauvais distanceUnit Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature index 7a08241..5feac4a 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-smartrouting.feature @@ -81,4 +81,20 @@ Feature: Road2-SMARTROUTING Examples: | method | | GET | - | POST | \ No newline at end of file + | POST | + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec geometryFormat=wkt + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-smartpgr" + And with query parameters: + | key | value | + | geometryFormat | wkt | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the response should contain a string attribute "geometry" + Examples: + | method | + | GET | + | POST | \ No newline at end of file diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature index 3cb0a4e..3a36259 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-valhalla.feature @@ -51,6 +51,22 @@ Feature: Road2-Valhalla | GET | | POST | + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 avec geometryFormat=wkt + Given an "" request on operation "isochrone" in api "simple" "1.0.0" + And with default parameters for "isochrone-valhalla" + And with query parameters: + | key | value | + | geometryFormat | wkt | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid iso + And the response should contain a string attribute "geometry" + Examples: + | method | + | GET | + | POST | + Scenario Outline: [] Route sur l'API simple 1.0.0 avec valhalla Given an "" request on operation "route" in api "simple" "1.0.0" And with default parameters for "route-valhalla" @@ -63,3 +79,21 @@ Scenario Outline: [] Route sur l'API simple 1.0.0 avec valhalla | method | | GET | | POST | + + Scenario Outline: [] Route sur l'API simple 1.0.0 avec geometryFormat=wkt + Given an "" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-valhalla" + And with query parameters: + | key | value | + | geometryFormat | wkt | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain a string attribute "geometry" + Examples: + | method | + | GET | + | POST | + + diff --git a/test/integration/mocha/geometry/integrationLine.js b/test/integration/mocha/geometry/integrationLine.js index 8396969..45bf2d9 100644 --- a/test/integration/mocha/geometry/integrationLine.js +++ b/test/integration/mocha/geometry/integrationLine.js @@ -55,14 +55,6 @@ describe('Test de la classe Line', function() { describe('Changement de format', function() { - it('getGeoJSON()', function() { - assert.deepEqual(line.getGeoJSON(), refGeojson); - }); - - it('getEncodedPolyline()', function() { - assert.deepEqual(line.getEncodedPolyline(), refPolyline); - }); - it('getGeometryWithFormat()', function() { assert.deepEqual(line.getGeometryWithFormat("geojson"), refGeojson); }); From 690e1d9de2ff7175eb2949ffa689e6e0ebdc1aa6 Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 20 Dec 2022 12:07:52 +0100 Subject: [PATCH 37/93] [test] fix de la conf smartpgr --- .../request/cucumber/configurations/local-service.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/functional/request/cucumber/configurations/local-service.json b/test/functional/request/cucumber/configurations/local-service.json index fb332bc..f40e7ae 100644 --- a/test/functional/request/cucumber/configurations/local-service.json +++ b/test/functional/request/cucumber/configurations/local-service.json @@ -42,7 +42,9 @@ "id": "isochrone-smartpgr", "parameters": { "resource": "bduni-idf-smartpgr", - "point": "2.333865,48.881989" + "point": "2.333865,48.881989", + "costValue": "100", + "costType": "time" } }, { From b682c44d156382565283d51fa77496670d6189a9 Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 20 Dec 2022 13:08:38 +0100 Subject: [PATCH 38/93] [doc] maj du changelog.md --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index d2482c9..e3ee51e 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ ADDED: - Cette classe est configurée par un nouveau fichier de configuration. - Ajout du moteur Valhalla pour les itinéraires et les isochrones - Le module `wkt` a été remplacé par une implémentation interne + - Le format wkt est disponible pour le paramètre geometryFormat de l'API simple/1.0.0 CHANGED: - L'option --configCheck au démarrage de Road2 n'a plus exactement le même comportement. From 4af0ddf7c7dc4da1b9e49466d4f9718b0a4d61f2 Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 20 Dec 2022 13:19:52 +0100 Subject: [PATCH 39/93] [fix] docker-compose command pour road2 --- docker/dev/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index 9c4c2fa..a7869ec 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -11,7 +11,7 @@ services: - pgrouting environment: - NODE_ENV=debug - command : "sleep 60000" + command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" ports: - 8080:8080 - 9229:9229 From dabdba688902b6b8ae5c6f1ffd82bc7f863d14aa Mon Sep 17 00:00:00 2001 From: azarz Date: Mon, 19 Dec 2022 17:28:12 +0100 Subject: [PATCH 40/93] feature(valhalla-source): add contraints to isochrone --- docker/distributions/debian/Dockerfile | 2 +- src/js/sources/valhallaSource.js | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index ab2644b..c4374a0 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -15,7 +15,7 @@ cmake -B build . && cmake --build build && make -C build install WORKDIR /home/valhalla/ RUN pip install --upgrade conan -RUN git clone --branch feature-exclude_bridges/tunnels/toll --depth 1 --recursive https://github.com/IGNF/valhalla.git && cd valhalla && \ +RUN git clone --branch 3.2.0-with_hard_exclude --depth 1 --recursive https://github.com/IGNF/valhalla.git && cd valhalla && \ mkdir build && cmake -B build -DCMAKE_BUILD_TYPE=Release && make -C build && make -C build package FROM node:16-bullseye as road2 diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js index 1336f06..b02250d 100644 --- a/src/js/sources/valhallaSource.js +++ b/src/js/sources/valhallaSource.js @@ -42,7 +42,7 @@ module.exports = class valhallaSource extends Source { // Stockage de la configuration this._configuration = sourceJsonObject; - // Gestions des coûts disponibles + // Gestions des coûts disponibles this._costs = {}; // Initialisation des coûts @@ -275,10 +275,18 @@ module.exports = class valhallaSource extends Source { const locationsString = `"locations":[{"lat":${tmpPoint[1]},"lon":${tmpPoint[0]}}]`; const costingString = `"costing":"${this._costs[request.profile][request.costType].costing}"`; + let costingOptionsString = `"costing_options":{"${this._costs[request.profile][request.costType].costing}":{`; + for (let i = 0; i < constraints.length; i++) { + costingOptionsString += `"${constraints[i]}": "1"` + if (i != constraints.length - 1) { + costingOptionsString += "," + } + } + costingOptionsString += "}}" const contoursString = `"contours":[{"${request.costType}":${costValue}}]`; const reverseString = `"reverse":${reverse}`; const polygonsString = `"polygons":true`; - const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${contoursString},${reverseString},${polygonsString}}' `; + const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${costingOptionsString},${contoursString},${reverseString},${polygonsString}}' `; LOGGER.info(commandString); return new Promise( (resolve, reject) => { @@ -542,7 +550,7 @@ module.exports = class valhallaSource extends Source { * */ writeIsochroneResponse(isochroneRequest, valhallaResponseStr) { - + let point = {}; let geometry = {}; let valhallaResponse; @@ -568,7 +576,7 @@ module.exports = class valhallaSource extends Source { LOGGER.debug("data projection: " + super.projection); - // Création d'un objet Point + // Création d'un objet Point point = isochroneRequest.point; if (!point.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of point in Valhalla response"); From e1685d3a7bb3a108636de1415479a99a4dc1a0de Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Thu, 22 Dec 2022 17:46:37 +0100 Subject: [PATCH 41/93] (submodules) using develop branch of other projects --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3174efe..2d208c3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ [submodule "docker/route-graph-generator"] path = docker/route-graph-generator url = https://github.com/IGNF/route-graph-generator.git - branch = master + branch = develop [submodule "docker/motors/pgrouting-procedures"] path = docker/motors/pgrouting-procedures url = https://github.com/IGNF/pgrouting-procedures.git - branch = master + branch = develop From 6a6b7e38421ead01aadac6541e253ce53a352167 Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Mon, 26 Dec 2022 20:23:05 +0100 Subject: [PATCH 42/93] update submodules --- docker/motors/pgrouting-procedures | 2 +- docker/route-graph-generator | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/motors/pgrouting-procedures b/docker/motors/pgrouting-procedures index f9bee79..261ca48 160000 --- a/docker/motors/pgrouting-procedures +++ b/docker/motors/pgrouting-procedures @@ -1 +1 @@ -Subproject commit f9bee79dcafd4820b20132a9361cf86b58bf7264 +Subproject commit 261ca48c5f13a663dca6e4971e5775e4c9487c47 diff --git a/docker/route-graph-generator b/docker/route-graph-generator index d403450..d197411 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit d403450fda103cc67a97a4be8889d25fe045fbd7 +Subproject commit d19741120b915d1cf034e099fe5ccad1cbb63e0c From 87b771099100d7c91b08b54a156c3e90ed91260c Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Tue, 27 Dec 2022 17:27:34 +0100 Subject: [PATCH 43/93] Working dependency in test/www --- test/www/isochrone.html | 16 ++++--- test/www/itineraire.html | 14 +++--- test/www/js/isochrone.js | 92 ++++++++++++++++++++-------------------- 3 files changed, 62 insertions(+), 60 deletions(-) diff --git a/test/www/isochrone.html b/test/www/isochrone.html index 98cc43f..7d6e7f8 100644 --- a/test/www/isochrone.html +++ b/test/www/isochrone.html @@ -29,7 +29,7 @@
- +

Paramètres de l'isochrone

@@ -39,6 +39,8 @@ + +

Mode de déplacement :

@@ -96,7 +98,7 @@

- +

@@ -106,7 +108,7 @@

- +
@@ -120,7 +122,7 @@

- +
@@ -131,10 +133,10 @@

- +
- + @@ -150,7 +152,7 @@ - + diff --git a/test/www/itineraire.html b/test/www/itineraire.html index ca6ac06..af084e0 100644 --- a/test/www/itineraire.html +++ b/test/www/itineraire.html @@ -28,7 +28,7 @@
- +

Ressource :

@@ -92,7 +92,7 @@

- +

@@ -102,7 +102,7 @@

- +
@@ -116,7 +116,7 @@

- +
@@ -127,10 +127,10 @@

- +
- + @@ -146,7 +146,7 @@ - + diff --git a/test/www/js/isochrone.js b/test/www/js/isochrone.js index ac80c35..5983b27 100644 --- a/test/www/js/isochrone.js +++ b/test/www/js/isochrone.js @@ -13,16 +13,16 @@ var defaultDirection = "departure"; var defaultGraphName = "Voiture"; var defaultMethod = "time"; var defaultReverse = "false" -// -- +// -- -// -- Variables pour la carte +// -- Variables pour la carte // Vecteurs qui vont contenir les éléments de l'itineraire sur la carte var vectorRoad = new ol.source.Vector(); var vectorRoadOther = new ol.source.Vector(); var vectorPoint = new ol.source.Vector(); -// Couches de la carte qui vont afficher l'itinéraire +// Couches de la carte qui vont afficher l'itinéraire var vectorRoadLayer = new ol.layer.Vector({ source: vectorRoad }); @@ -35,7 +35,7 @@ var vectorPointLayer = new ol.layer.Vector({ source: vectorPoint }); -// Styles pour les marqueurs +// Styles pour les marqueurs var styles = { routePolyline: new ol.style.Style({ stroke: new ol.style.Stroke({ @@ -55,17 +55,17 @@ var styles = { }) }; -// -- +// -- // -- Creation de la carte à la fin du chargement de la page Gp.Services.getConfig({ apiKey: "pratique", onSuccess: createMap }); -// -- +// -- // ---- -// ---- Création d'un nouveau menu contextuel sur la carte +// ---- Création d'un nouveau menu contextuel sur la carte var contextMenuItems = [ { @@ -75,7 +75,7 @@ var contextMenuItems = [ ]; -// ---- +// ---- // Fonction pour créer la carte function createMap() { @@ -155,7 +155,7 @@ function createMap() { // Ajout du controle à la carte map.addControl(mpControl); - // Ajout du menu contextuel à la carte + // Ajout du menu contextuel à la carte var contextmenu = new ContextMenu({ width: 180, items: contextMenuItems @@ -164,8 +164,8 @@ function createMap() { } -// ---- Ajouter un point sur la carte -// Fonction utilisée lors d'un clique droit sur la carte +// ---- Ajouter un point sur la carte +// Fonction utilisée lors d'un clique droit sur la carte // Il s'agit d'afficher un marqueur et de stocker les coordonnées de ce point // Et tout cela en intéragissant avec le formulaire des paramètres de l'isochrone function definePoint(evt) { @@ -176,7 +176,7 @@ function definePoint(evt) { if (clickedPoint.length !== 0) { clickedPoint = new Array(); vectorPoint.clear(); - } + } // on stocke les coordonnées pour pouvoir lancer un isochrone clickedPoint.push(clickedCoordinate); @@ -192,11 +192,11 @@ function definePoint(evt) { } -// ---- +// ---- // ---- Calculer un isochrone -// Cette fonction est appelée lorsque l'on clique sur un des boutons du formulaire +// Cette fonction est appelée lorsque l'on clique sur un des boutons du formulaire function computeIso() { // Déclarations @@ -210,16 +210,16 @@ function computeIso() { if ( request.finalPoint === "" || request.finalCostValue === "") { // il n'y a pas de points ou de valeur pour l'isochrone return false; - } + } // -- Gestion des paramètres de l'utilisateur // Si certains paramètres ne sont pas remplis dans le formulaire, il y a des valeurs par défaut loadUserParameter(request); - + // -- - // ---- Requete envoyée au nouveau service + // ---- Requete envoyée au nouveau service let requestStr = road2Url + "resource=" + request.finalResource + "&profile=" + request.finalProfile + @@ -230,9 +230,9 @@ function computeIso() { "&constraints=" + request.finalConstraint + "&geometryFormat=geojson"; - // On affiche la requete sur la page + // On affiche la requete sur la page let requestDiv = document.getElementById('request'); - requestDiv.innerHTML = ""; + requestDiv.innerHTML = ""; // on calcule l'itinéraire fetch(requestStr) @@ -243,26 +243,26 @@ function computeIso() { utils.createIso(responseJSON.geometry, "geojson", vectorRoad); - // On affiche la réponse sur la page + // On affiche la réponse sur la page let responseDiv = document.getElementById('response'); responseDiv.innerHTML = "
"+ JSON.stringify(responseJSON, undefined, 2) +"
"; - + }); - // ---- + // ---- - // ---- Requete envoyée à un autre service + // ---- Requete envoyée à un autre service // Si c'est demandé if (document.forms["comparison-form"].elements["compare-to-old"].checked) { computeOtherRoad(request); } - // ---- + // ---- return true; } -// ---- +// ---- // ---- Calculer un itinéraire sur un autre service @@ -270,11 +270,11 @@ function computeOtherRoad(request) { let otherRequest = {}; - // -- Prise en compte des paramètres utilisateurs + // -- Prise en compte des paramètres utilisateurs mapOtherParameter(request, otherRequest); - - // -- + + // -- let requestStr = oldUrl + "graphName=" + otherRequest.finalGraphName + @@ -292,10 +292,10 @@ function computeOtherRoad(request) { return false; } - // On affiche la requete sur la page + // On affiche la requete sur la page let requestDiv = document.getElementById('request-other'); requestDiv.innerHTML = ""; - + // on calcule l'itinéraire fetch(requestStr) @@ -306,11 +306,11 @@ function computeOtherRoad(request) { }) .then(function(responseJSON) { - // On affiche la réponse sur la page + // On affiche la réponse sur la page let responseDiv = document.getElementById('response-other'); responseDiv.innerHTML = "
"+ JSON.stringify(responseJSON, undefined, 2) +"
"; - // On affiche l'itinéraire sur la carte + // On affiche l'itinéraire sur la carte utils.createIso(responseJSON.wktGeometry, "wkt", vectorRoadOther); @@ -320,13 +320,13 @@ function computeOtherRoad(request) { } // ---- -// ---- Charger les paramètres de l'utilisateur +// ---- Charger les paramètres de l'utilisateur function loadUserParameter(request) { let constraintObject = {}; -// Resource +// Resource if (document.forms["iso-form"].elements["userResource"].value !== "") { request.finalResource = document.forms["iso-form"].elements["userResource"].value; } else { @@ -387,9 +387,9 @@ function loadUserParameter(request) { } -// ---- +// ---- -// ---- Faire le lien avec l'autre service +// ---- Faire le lien avec l'autre service function mapOtherParameter(request, otherRequest) { // Profile @@ -434,7 +434,7 @@ function mapOtherParameter(request, otherRequest) { if (document.forms["iso-form"].elements["banned-highway"].checked) { otherRequest.finalConstraint = "Toll"; other = true; - } + } if (document.forms["iso-form"].elements["banned-tunnel"].checked) { if (other) { otherRequest.finalConstraint = otherRequest.finalConstraint + ";"; @@ -446,7 +446,7 @@ function mapOtherParameter(request, otherRequest) { if (document.forms["iso-form"].elements["banned-bridge"].checked) { if (other) { otherRequest.finalConstraint = otherRequest.finalConstraint + ";"; - } + } otherRequest.finalConstraint = otherRequest.finalConstraint + "Bridge"; } @@ -455,13 +455,13 @@ function mapOtherParameter(request, otherRequest) { // ---- // ---- Supprimer l'itinéraire affiché sur la carte -// Cette fonction est appelée lorsque l'on clique sur un des boutons du formulaire +// Cette fonction est appelée lorsque l'on clique sur un des boutons du formulaire function cancelMap() { // Nettoyage des vecteurs qui contiennent les données sur la map vectorRoad.clear(); vectorRoadOther.clear(); - // Nettoyage des div qui concernent les isochrones supprimés + // Nettoyage des div qui concernent les isochrones supprimés let currentDiv = document.getElementById('request'); currentDiv.innerHTML = ""; currentDiv = document.getElementById('request-other'); @@ -472,14 +472,14 @@ function cancelMap() { currentDiv.innerHTML = ""; } -// ---- +// ---- // ---- Supprimer les paramètres du formulaire -// Cette fonction est appelée lorsque l'on clique sur un des boutons du formulaire +// Cette fonction est appelée lorsque l'on clique sur un des boutons du formulaire function cancelForm() { // Nettoyage du formulaire - document.getElementById("iso-form").reset(); + document.getElementById("iso-form").reset(); // Nettoyage des tableaux qui contiennent les points clickedPoint = new Array(); @@ -489,7 +489,7 @@ function cancelForm() { } -// ---- +// ---- var utils = { @@ -539,7 +539,7 @@ var utils = { } else { return false; } - + let feature = new ol.Feature({ type: 'route', @@ -555,7 +555,7 @@ var utils = { } else { } - + vector.addFeature(feature); } From d98b0b7e1af1c273a4d6ffc557abf139189f3d9f Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 3 Jan 2023 11:53:49 +0100 Subject: [PATCH 44/93] [fix] divers fix suite au eslint, sans submodules --- docker/dev/readme.md | 6 ++---- eslint.json | 4 +++- package.json | 3 ++- src/js/administrator/administrator.js | 3 +-- .../apis/simple/1.0.0/controller/controller.js | 9 ++++----- src/js/apis/simple/1.0.0/index.js | 1 + src/js/geography/projectionManager.js | 5 +---- src/js/geometry/line.js | 2 +- src/js/parameters/boolParameter.js | 7 ++++--- src/js/parameters/constraintParameter.js | 5 ++--- src/js/parameters/enumParameter.js | 5 ++--- src/js/parameters/floatParameter.js | 2 +- src/js/parameters/intParameter.js | 1 - src/js/parameters/resourceParameter.js | 10 +++++++--- src/js/resources/resource.js | 7 +++++++ src/js/service/service.js | 7 ++++--- src/js/sources/pgrSource.js | 18 ++++++++---------- src/js/sources/smartroutingSource.js | 2 +- src/js/sources/source.js | 2 ++ src/js/sources/sourceManager.js | 2 +- src/js/utils/logManager.js | 3 +-- src/js/utils/mathManager.js | 2 +- 22 files changed, 56 insertions(+), 50 deletions(-) diff --git a/docker/dev/readme.md b/docker/dev/readme.md index e6508f5..c492e54 100644 --- a/docker/dev/readme.md +++ b/docker/dev/readme.md @@ -26,10 +26,8 @@ Si on utilise ces Dockerfile avec un VPN, on vérifiera que les configurations D ### IP Si on utilise ces Dockerfile sur un réseau avec lequel il peut y avoir des problèmes d'IP, il sera utile de dédier à Docker une plage d'IP non utilisées: - L'attribut `bip` du fichier ``/etc/docker/daemon.json` permet de préciser une plage d'IP. -- Si bip a été rempli, on veillera à ce que ces IP soient bien ajouter à l'interface `docker0`. La commande `sudo ip route add {plage_ip} dev docker0` permet de le faire. -- On pourra aussi avoir besoin d'ajouter le réseau créer par ce compose : `sudo ip route add {plage_ip} dev br-{id_du_network} proto kernel scope link src {ip_gateway}` où l'id est obtenu en faisant un `docker network ls`. - -Cette plage d'IP sera différente de celle attribuée à la stack Road2 lancée via docker-compose (eg. celui présent dans le fichier `.env`). +- Si bip a été rempli, on veillera à ce que cette plage d'IP soit bien ajouté à l'interface `docker0`. La commande `sudo ip route add {plage_ip} dev docker0` permet de le faire. +- On pourra aussi avoir besoin d'ajouter une plage d'IP différente pour utiliser ce compose : `sudo ip route add {plage_ip_env} dev br-{id_du_network} proto kernel scope link src {ip_env_gateway}` où l'id est obtenu en faisant un `docker network ls`. La plage d'IP et sa porte sont celles définies dans le `.env`. ### HTTPS Si on souhaite tester le serveur en HTTPS, certaines actions sont nécessaires en amont: diff --git a/eslint.json b/eslint.json index ce511d3..50ed062 100644 --- a/eslint.json +++ b/eslint.json @@ -12,10 +12,12 @@ } }, "extends": "eslint:recommended", + "parser":"esprima", "rules": { "strict": 1, "eqeqeq": 1, "curly": 1, - "no-console": 0 + "no-console": 1, + "no-unused-vars": 1 } } diff --git a/package.json b/package.json index 4ccaf25..9e5f0bf 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "mock-fs": "^4.9.0", "cucumber": "5.1.0", "axios": "^0.21.1", - "tunnel": "^0.0.6" + "tunnel": "^0.0.6", + "esprima":"4.0.1" }, "bundledDependencies": [ "cors", diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 32ba3bd..89bf1d2 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -60,11 +60,10 @@ module.exports = class Administrator { * @name checkAdminConfiguration * @description Vérifier la partie administration de la configuration * @param {json} configuration - Configuration de Road2 (contenu du server.json) - * @param {string} configurationPath - Chemin de la configuration de Road2 (chemin du server.json) * */ - checkAdminConfiguration(configuration, configurationPath) { + checkAdminConfiguration(configuration) { LOGGER.info("Verification de la configuration de l'administrateur..."); diff --git a/src/js/apis/simple/1.0.0/controller/controller.js b/src/js/apis/simple/1.0.0/controller/controller.js index c75bcd0..a53d42f 100644 --- a/src/js/apis/simple/1.0.0/controller/controller.js +++ b/src/js/apis/simple/1.0.0/controller/controller.js @@ -485,12 +485,11 @@ module.exports = { * @description Vérification des paramètres d'une requête sur /nearest * @param {object} parameters - ensemble des paramètres de la requête * @param {object} service - Instance de la classe Service - * @param {string} method - Méthode de la requête * @return {object} NearestRequest - Instance de la classe NearestRequest * */ - checkNearestParameters: function(parameters, service, method) { + checkNearestParameters: function(parameters, service) { let resource; let coordinates = {}; @@ -709,7 +708,7 @@ module.exports = { /* Vérification de la validité des coordonnées fournies. */ let validity = isochroneOperation.getParameterById("point").check(parameters.point, askedProjection); - if (validity.code != "ok") { + if (validity.code !== "ok") { throw errorManager.createError("Parameter 'point' is invalid: " + validity.message, 400); } else { @@ -733,7 +732,7 @@ module.exports = { /* Vérification de la validité du paramètre fourni. */ let validity = isochroneOperation.getParameterById("costType").check(parameters.costType); - if (validity.code != "ok") { + if (validity.code !== "ok") { throw errorManager.createError("Parameter 'costType' is invalid: " + validity.message, 400); } else { costType = parameters.costType; @@ -752,7 +751,7 @@ module.exports = { /* Vérification de la validité du paramètre fourni. */ let validity = isochroneOperation.getParameterById("costValue").check(parameters.costValue); - if (validity.code != "ok") { + if (validity.code !== "ok") { throw errorManager.createError("Parameter 'costValue' is invalid: " + validity.message, 400); } else { diff --git a/src/js/apis/simple/1.0.0/index.js b/src/js/apis/simple/1.0.0/index.js index d59ecf7..b291e24 100644 --- a/src/js/apis/simple/1.0.0/index.js +++ b/src/js/apis/simple/1.0.0/index.js @@ -64,6 +64,7 @@ router.all("/getcapabilities", function(req, res) { // il est récupéré par express dans req.host if (req.hostname) { + // TODO : corriger avec quelque chose du genre ^http(s)?:\/\/(.+) puis split au premier / let regexpHost = /^http[s]?:\/\/[\w\d:-_\.]*\//; try { diff --git a/src/js/geography/projectionManager.js b/src/js/geography/projectionManager.js index fdd4a88..dd47f5d 100644 --- a/src/js/geography/projectionManager.js +++ b/src/js/geography/projectionManager.js @@ -107,10 +107,9 @@ module.exports = class ProjectionManager { * @name checkBboxConfiguration * @description Savoir si une bbox est valide dans une projection donnée * @param {string} bbox - Bbox à vérifier - * @param {string} projectionId - ID de la projection * */ - checkBboxConfiguration (bbox, projectionId) { + checkBboxConfiguration (bbox) { LOGGER.info("Vérification d'une bbox... "); LOGGER.debug("bbox:'" + bbox+"'"); @@ -154,8 +153,6 @@ module.exports = class ProjectionManager { return false; } - // TODO : vérifier la cohérence avec la projection indiquée - LOGGER.info("Vérification de la bbox terminée"); return true; diff --git a/src/js/geometry/line.js b/src/js/geometry/line.js index e03f4e2..024154b 100644 --- a/src/js/geometry/line.js +++ b/src/js/geometry/line.js @@ -39,7 +39,7 @@ module.exports = class Line extends Geometry { // Format de géométrie (geojson, polyline...) this._format = format; - if (polylinePrecision != 5 && this._format === "polyline") { + if (polylinePrecision !== 5 && this._format === "polyline") { this._geom = polyline.encode(polyline.decode(geom, polylinePrecision)); } diff --git a/src/js/parameters/boolParameter.js b/src/js/parameters/boolParameter.js index db60e3e..a1a710b 100644 --- a/src/js/parameters/boolParameter.js +++ b/src/js/parameters/boolParameter.js @@ -84,16 +84,15 @@ module.exports = class BoolParameter extends ResourceParameter { /** * * @function - * @name check + * @name specificCheck * @description Vérifier la validité d'une valeur par rapport au paramètre * @param {string} userValue - Valeur à vérifier - * @param {object} options - Options * @return {object} result.code - "ok" si tout s'est bien passé et "error" sinon * result.message - "" si tout s'est bien passé et la raison de l'erreur sinon * * */ - specificCheck(userValue, options) { + specificCheck(userValue) { LOGGER.debug("specificCheck()"); @@ -110,6 +109,8 @@ module.exports = class BoolParameter extends ResourceParameter { return validationManager.createValidationMessage(""); } + + } /** diff --git a/src/js/parameters/constraintParameter.js b/src/js/parameters/constraintParameter.js index 3cb9dbb..f31070c 100644 --- a/src/js/parameters/constraintParameter.js +++ b/src/js/parameters/constraintParameter.js @@ -221,13 +221,12 @@ module.exports = class ConstraintParameter extends ResourceParameter { * @name specificCheck * @description Vérifier la validité d'une valeur par rapport au paramètre * @param {string} userValue - Valeur à vérifier - * @param {object} options - Options * @return {object} result.code - "ok" si tout s'est bien passé et "error" sinon * result.message - "" si tout s'est bien passé et la raison de l'erreur sinon * * */ - specificCheck(userValue, options) { + specificCheck(userValue) { LOGGER.debug("specificCheck()"); @@ -294,7 +293,7 @@ module.exports = class ConstraintParameter extends ResourceParameter { } // Gestion du champ costRatio pour contraintes préférentielles - if (userJson.constraintType === 'avoid' || userJson.constraintType === 'prefer') { + if (userJson.constraintType === 'avoid' || userJson.constraintType === 'prefer') { if (userJson.costRatio) { diff --git a/src/js/parameters/enumParameter.js b/src/js/parameters/enumParameter.js index 2d1dac1..2228da3 100644 --- a/src/js/parameters/enumParameter.js +++ b/src/js/parameters/enumParameter.js @@ -86,16 +86,15 @@ module.exports = class EnumParameter extends ResourceParameter { /** * * @function - * @name check + * @name specificCheck * @description Vérifier la validité d'une valeur par rapport au paramètre * @param {string} userValue - Valeur à vérifier - * @param {object} options - Options * @return {object} result.code - "ok" si tout s'est bien passé et "error" sinon * result.message - "" si tout s'est bien passé et la raison de l'erreur sinon * * */ - specificCheck(userValue, options) { + specificCheck(userValue) { LOGGER.debug("specificCheck()"); diff --git a/src/js/parameters/floatParameter.js b/src/js/parameters/floatParameter.js index 1376888..26ec6f6 100644 --- a/src/js/parameters/floatParameter.js +++ b/src/js/parameters/floatParameter.js @@ -101,7 +101,7 @@ module.exports = class FloatParameter extends ResourceParameter { /** * * @function - * @name check + * @name specificCheck * @description Vérifier la validité d'une valeur par rapport au paramètre * @param {string} userValue - Valeur à vérifier * @return {object} result.code - "ok" si tout s'est bien passé et "error" sinon diff --git a/src/js/parameters/intParameter.js b/src/js/parameters/intParameter.js index 27cadf5..a9f4283 100644 --- a/src/js/parameters/intParameter.js +++ b/src/js/parameters/intParameter.js @@ -4,7 +4,6 @@ const ResourceParameter = require('./resourceParameter'); const log4js = require('log4js'); const errorManager = require('../utils/errorManager'); const validationManager = require('../utils/validationManager'); -const mathManager = require('../utils/mathManager'); var LOGGER = log4js.getLogger("INTPARAM"); diff --git a/src/js/parameters/resourceParameter.js b/src/js/parameters/resourceParameter.js index a9dd4ad..c961feb 100644 --- a/src/js/parameters/resourceParameter.js +++ b/src/js/parameters/resourceParameter.js @@ -54,9 +54,8 @@ module.exports = class ResourceParameter { * */ load(parameterConf) { - + LOGGER.debug("configuration du parametre : " + parameterConf.toString()); return false; - } /** @@ -65,7 +64,7 @@ module.exports = class ResourceParameter { * @name check * @description Vérifier la validité d'une valeur par rapport au paramètre * @param {string} userValue - Valeur à vérifier - * @param {object} options - Options + * @param {object|string} options - Options * @return {object} result.code - "ok" si tout s'est bien passé et "error" sinon * result.message - "" si tout s'est bien passé et la raison de l'erreur sinon * @@ -167,6 +166,8 @@ module.exports = class ResourceParameter { specificCheck(userValue, options) { LOGGER.debug("specificCheck()"); + LOGGER.debug("user value : " + userValue); + LOGGER.debug("options: " + options.toString()); return errorManager.createErrorMessage(""); } @@ -227,6 +228,9 @@ module.exports = class ResourceParameter { */ specificConvertion(userValue, options) { + LOGGER.debug("specificConversion()"); + LOGGER.debug("user value : " + userValue); + LOGGER.debug("options: " + options.toString()); return null; } diff --git a/src/js/resources/resource.js b/src/js/resources/resource.js index 73fd087..cb96d5d 100644 --- a/src/js/resources/resource.js +++ b/src/js/resources/resource.js @@ -1,5 +1,9 @@ 'use strict'; +const log4js = require('log4js'); + +var LOGGER = log4js.getLogger("RESOURCE"); + /** * * @class @@ -95,6 +99,7 @@ module.exports = class Resource { */ getSourceIdFromRequest (request) { let sourceId = ""; + LOGGER.debug(request.toString()); return sourceId; } @@ -111,6 +116,7 @@ module.exports = class Resource { * */ checkSourceAvailibilityFromRequest (request) { + LOGGER.debug(request.toString()); return false; } @@ -127,6 +133,7 @@ module.exports = class Resource { * */ initResource (sourceManager) { + LOGGER.debug(sourceManager.toString()); return false; } diff --git a/src/js/service/service.js b/src/js/service/service.js index 096314f..78cfbf0 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -289,7 +289,7 @@ module.exports = class Service { return false; } - if (!LogManager.checkLogConfiguration(logConf, logConfPath)) { + if (!LogManager.checkLogConfiguration(logConf)) { LOGGER.error("Le logger est mal configuré"); return false; } @@ -927,7 +927,7 @@ module.exports = class Service { * @description Fonction utilisée pour rediriger une requête vers le bon moteur. * Une requête se fait nécessairement sur une ressource. Cette ressource est indiquée dans la requête. * La ressource sait comment déterminer la source concernée par la requête. - * Et la source interrogera le moteur. + * Et la source interrogera le moteur. * @param {Request} request - Requête * */ @@ -956,7 +956,8 @@ module.exports = class Service { try { return source.computeRequest(request); } catch(err) { - return err + // TODO : modifier ce comportement + return err; } // --- diff --git a/src/js/sources/pgrSource.js b/src/js/sources/pgrSource.js index 0874a52..5e27990 100644 --- a/src/js/sources/pgrSource.js +++ b/src/js/sources/pgrSource.js @@ -431,7 +431,7 @@ module.exports = class pgrSource extends Source { point = JSON.stringify(request.point.getCoordinatesIn(super.projection)); } catch(error) { LOGGER.error(error); - reject(errorManager.createError("Impossible de récupérer les coordonnées du point")); + throw errorManager.createError("Impossible de récupérer les coordonnées du point"); } let constraints = ""; @@ -519,15 +519,13 @@ module.exports = class pgrSource extends Source { } else { - // TODO: qu'est-ce qui se passe si on arrive là, doit-on retourner une erreur ou une promesse ? - LOGGER.error("type of request not found"); + throw errorManager.createError("type of request not found"); } } else { - // TODO: qu'est-ce qui se passe si on arrive là, doit-on retourner une erreur ou une promesse ? - LOGGER.error("request operation not found"); + throw errorManager.createError("request operation not found"); } @@ -657,7 +655,7 @@ module.exports = class pgrSource extends Source { for (let rowIdx = 0; rowIdx < pgrResponse.rows.length; rowIdx++) { row = pgrResponse.rows[rowIdx]; - if (row.path_seq != lastPathSeq) { + if (row.path_seq !== lastPathSeq) { // TODO: Il n'y a qu'une route pour l'instant: à changer pour plusieurs routes response.routes[0].legs.push( { steps: [], geometry: {type: "LineString", coordinates: [] }, duration: 0, distance: 0 } ); // Si ce n'est pas la première leg, il faut ajouter la dernière géométrie parcourue (pour faire le lien) @@ -729,7 +727,7 @@ module.exports = class pgrSource extends Source { } // Gestion des derniers points intermédiaires sur le même tronçon que le point final (ticket #34962) - if (rowIdx == pgrResponse.rows.length - 1) { + if (rowIdx === pgrResponse.rows.length - 1) { while (response.routes[0].legs.length < response.waypoints.length - 1) { response.routes[0].legs.push( { steps: [], geometry: {type: "LineString", coordinates: [] }, duration: 0, distance: 0 } ); // Cas possible de problème dans les données : le tronçon n'a pas de géométrie @@ -880,7 +878,7 @@ module.exports = class pgrSource extends Source { let currentPgrRouteStepDistance = turf.length(currentPgrRouteStep.geometry); // Troncature de la géométrie : cas où il n'y a qu'un step - if (k == 0 && currentPgrRouteLeg.steps.length == 1){ + if (k === 0 && currentPgrRouteLeg.steps.length === 1){ let stepStart = turf.point(response.waypoints[j].location); let stepEnd = turf.point(response.waypoints[j + 1].location); @@ -900,7 +898,7 @@ module.exports = class pgrSource extends Source { } // Troncature de la géométrie : cas de début de leg - else if (k == 0){ + else if (k === 0){ let stepStart = turf.point(response.waypoints[j].location); currentPgrRouteStep.geometry.coordinates = turf.truncate( turf.lineSlice( @@ -921,7 +919,7 @@ module.exports = class pgrSource extends Source { } // Troncature de la géométrie : cas de fin de leg - else if (k == currentPgrRouteLeg.steps.length - 1) { + else if (k === currentPgrRouteLeg.steps.length - 1) { let stepEnd = turf.point(response.waypoints[j+1].location); // Pour le cas des boucles, il faut tester si l'intersection entre le dernier tronçon diff --git a/src/js/sources/smartroutingSource.js b/src/js/sources/smartroutingSource.js index be53d34..b0bd172 100644 --- a/src/js/sources/smartroutingSource.js +++ b/src/js/sources/smartroutingSource.js @@ -443,7 +443,7 @@ module.exports = class smartroutingSource extends Source { location = new Point(locationCoords[0], locationCoords[1], projection); // Geometrie - const rawGeometry = wkt.toGeoJSON(smartroutingResponse.wktGeometry); + let rawGeometry = wkt.toGeoJSON(smartroutingResponse.wktGeometry); // Cas où il n'y a pas d'isochrone car costValue trop faible if (rawGeometry === null) { diff --git a/src/js/sources/source.js b/src/js/sources/source.js index 318f597..a2ccbf0 100644 --- a/src/js/sources/source.js +++ b/src/js/sources/source.js @@ -1,5 +1,7 @@ 'use strict'; +const errorManager = require('../utils/errorManager'); + /** * * @class diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index 1956332..de76b91 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -306,7 +306,7 @@ module.exports = class sourceManager { return false; } else { // Vérification de la bbox - if (!this._projectionManager.checkBboxConfiguration(sourceJsonObject.bbox, sourceJsonObject.projection)) { + if (!this._projectionManager.checkBboxConfiguration(sourceJsonObject.bbox)) { LOGGER.error("La source indique une bbox incorrecte: " + sourceJsonObject.bbox); return false; } diff --git a/src/js/utils/logManager.js b/src/js/utils/logManager.js index c9bc972..7c8d209 100644 --- a/src/js/utils/logManager.js +++ b/src/js/utils/logManager.js @@ -13,12 +13,11 @@ module.exports = { * @name checkLogConfiguration * @description Vérification de la configuration d'un logger * @param {object} userLogConfiguration - Configuration du logger - * @param {string} userLogConfPath - Chemin absolu de la configuration du logger * @return {boolean} * */ - checkLogConfiguration: function(userLogConfiguration, userLogConfPath) { + checkLogConfiguration: function(userLogConfiguration) { LOGGER.info("Vérification de la configuration du logger..."); diff --git a/src/js/utils/mathManager.js b/src/js/utils/mathManager.js index f1e4686..4b29cd2 100644 --- a/src/js/utils/mathManager.js +++ b/src/js/utils/mathManager.js @@ -14,7 +14,7 @@ module.exports = { isFloat: function(value) { - if (/^(\-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) { + if (/^(-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) { return true; } else { return false; From 92c0a43cbd405a17e68368cade9ac968d93da36f Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Tue, 3 Jan 2023 17:26:29 +0100 Subject: [PATCH 45/93] fix Dockerfile + use fixed version of prime_server --- docker/distributions/debian/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index c4374a0..e0f8599 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -10,7 +10,7 @@ apt-get install -y libsqlite3-mod-spatialite python3-pip WORKDIR /home/prime-server RUN apt-get install -y git cmake autoconf automake pkg-config libtool make gcc g++ lcov libcurl4-openssl-dev libzmq3-dev libczmq-dev -RUN git clone --depth 1 --recursive https://github.com/kevinkreiser/prime_server.git && cd prime_server && \ +RUN git clone --depth 3 --recursive https://github.com/kevinkreiser/prime_server.git && cd prime_server && git checkout 43715e5 && \ cmake -B build . && cmake --build build && make -C build install WORKDIR /home/valhalla/ @@ -33,8 +33,8 @@ COPY --from=build /usr/local/lib/libprime_server.so.0 /usr/lib/libprime_server.s COPY --from=build /usr/local/lib/libprime_server.so /usr/lib/libprime_server.so ### Installation de valhalla -COPY --from=build /home/valhalla/valhalla/build/valhalla-3.1.4-Linux.tar.gz ./ -RUN tar -xzvf valhalla-3.1.4-Linux.tar.gz && cd valhalla-3.1.4-Linux && cp -r bin/* /usr/bin/ && cp -r lib/* /usr/lib/ && cp -r include/* /usr/include/ && cp -r share/* /usr/share/ +COPY --from=build /home/valhalla/valhalla/build/valhalla-3.2.0-Linux.tar.gz ./ +RUN tar -xzvf valhalla-3.2.0-Linux.tar.gz && cd valhalla-3.2.0-Linux && cp -r bin/* /usr/bin/ && cp -r lib/* /usr/lib/ && cp -r include/* /usr/include/ && cp -r share/* /usr/share/ ### Dossier contenant la configuration de Road2 WORKDIR /home/docker/config From c89aa167ad9698c49177b12fee376f6584d2dfaf Mon Sep 17 00:00:00 2001 From: "amaury.zarzelli" Date: Thu, 5 Jan 2023 12:14:44 +0100 Subject: [PATCH 46/93] update prime server clone since issue fix --- docker/distributions/debian/Dockerfile | 2 +- docker/route-graph-generator | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index e0f8599..fde30e6 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -10,7 +10,7 @@ apt-get install -y libsqlite3-mod-spatialite python3-pip WORKDIR /home/prime-server RUN apt-get install -y git cmake autoconf automake pkg-config libtool make gcc g++ lcov libcurl4-openssl-dev libzmq3-dev libczmq-dev -RUN git clone --depth 3 --recursive https://github.com/kevinkreiser/prime_server.git && cd prime_server && git checkout 43715e5 && \ +RUN git clone --depth 1 --recursive https://github.com/kevinkreiser/prime_server.git && cd prime_server && \ cmake -B build . && cmake --build build && make -C build install WORKDIR /home/valhalla/ diff --git a/docker/route-graph-generator b/docker/route-graph-generator index d197411..c9b526a 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit d19741120b915d1cf034e099fe5ccad1cbb63e0c +Subproject commit c9b526a1cb4d3972ecbdafac292c8e0c4cc0b242 From 584e88e0472cf85cd0d5ed5140c259a29d729585 Mon Sep 17 00:00:00 2001 From: Amaury Zarzelli Date: Fri, 6 Jan 2023 15:25:47 +0100 Subject: [PATCH 47/93] Create LICENSE.md --- LICENSE.md | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 03af50060bfa7fa7b4a2dc4e2e7b2c050ce1cd2f Mon Sep 17 00:00:00 2001 From: lgrd Date: Wed, 11 Jan 2023 14:27:03 +0100 Subject: [PATCH 48/93] Update of integration tests (#13) * [test] apisManager ok * [test] baseManager ok * [test] line ok * [test] resourceParameter ok * [test] serviceAdministered ok * [test] *Request ok * [test] serverManager ok * [test] serviceProcess en cours * [test] resourceOperation ok * [test] *parameter ok * [test] apisManager ok * [test] baseManager ok * [test] line ok * [test] resourceParameter ok * [test] serviceAdministered ok * [test] *Request ok * [test] serverManager ok * [test] serviceProcess en cours * [test] resourceOperation ok * [test] *parameter ok * [sub] update r2gg commit * fix(test) update json for pgr and valhalla source description * [doc] fix test/functional/readme.md Co-authored-by: jmkerloch <53606373+jmkerloch@users.noreply.github.com> Co-authored-by: jmkerloch Co-authored-by: jmkerloch <53606373+jmkerloch@users.noreply.github.com> --- docker/route-graph-generator | 2 +- src/js/apis/apisManager.js | 158 +----------------- src/js/base/baseManager.js | 22 --- src/js/geometry/line.js | 2 +- src/js/parameters/boolParameter.js | 2 +- src/js/parameters/constraintParameter.js | 2 +- src/js/parameters/enumParameter.js | 2 +- src/js/parameters/floatParameter.js | 2 +- src/js/parameters/intParameter.js | 2 +- src/js/parameters/pointParameter.js | 2 +- src/js/parameters/resourceParameter.js | 22 +-- src/js/requests/isochroneRequest.js | 26 +-- src/js/requests/nearestRequest.js | 10 +- src/js/requests/request.js | 17 +- src/js/requests/routeRequest.js | 13 +- src/js/server/serverManager.js | 11 +- src/js/service/serviceAdministered.js | 4 +- src/js/service/serviceProcess.js | 27 +-- test/functional/readme.md | 22 +-- .../mocha/apis/integrationApiManager.js | 55 +++--- .../mocha/base/integrationBaseManager.js | 42 +++-- test/integration/mocha/config/log4js.json | 4 +- test/integration/mocha/config/road2.json | 27 +++ .../mocha/config/service.json} | 40 +++-- .../mocha/geometry/integrationLine.js | 4 +- .../mocha/operations/integrationOperation.js | 2 +- .../integrationResourceOperation.js | 33 ++-- .../parameters/integrationBoolParameter.js | 82 +++++++++ .../integrationConstraintParameter.js | 76 +++++++++ .../parameters/integrationEnumParameter.js | 82 +++++++++ .../parameters/integrationFloatParameter.js | 110 ++++++++++++ .../parameters/integrationIntParameter.js | 110 ++++++++++++ .../parameters/integrationPointParameter.js | 92 ++++++++++ .../integrationResourceParameter.js | 57 +++++-- .../requests/integrationIsochroneRequest.js | 29 ++-- .../requests/integrationNearestRequest.js | 70 ++++++++ .../mocha/requests/integrationRouteRequest.js | 71 +++++++- .../resources/integrationOsrmResource.js | 2 +- .../resources/integrationResourceManager.js | 5 +- .../resources/integrationValhallaResource.js | 2 +- .../mocha/responses/integrationPortion.js | 2 +- .../mocha/server/integrationServerManager.js | 87 ++++++++++ .../mocha/service/integrationService.js | 4 +- .../service/integrationServiceProcess.js | 44 +++++ .../mocha/sources/integrationPgrSource.js | 20 ++- .../mocha/sources/integrationSourceManager.js | 2 +- .../sources/integrationValhallaSource.js | 3 +- .../topology/integrationTopologyManager.js | 87 ---------- test/integration/readme.md | 22 ++- .../resources/topology/correctDBTopology.json | 25 --- .../topology/correctOSMTopology.json | 10 -- .../topology/incorrectDBTopology.json | 24 --- .../topology/incorrectOSMTopology.json | 9 - .../mocha/config/resources/corse.resource | 130 -------------- test/unit/mocha/requests/testsRequest.js | 5 +- .../mocha/service/testsServiceAdministered.js | 26 +++ test/unit/readme.md | 5 +- 57 files changed, 1170 insertions(+), 678 deletions(-) create mode 100644 test/integration/mocha/config/road2.json rename test/{unit/mocha/config/road2.json => integration/mocha/config/service.json} (50%) create mode 100644 test/integration/mocha/parameters/integrationBoolParameter.js create mode 100644 test/integration/mocha/parameters/integrationConstraintParameter.js create mode 100644 test/integration/mocha/parameters/integrationEnumParameter.js create mode 100644 test/integration/mocha/parameters/integrationFloatParameter.js create mode 100644 test/integration/mocha/parameters/integrationIntParameter.js create mode 100644 test/integration/mocha/parameters/integrationPointParameter.js create mode 100644 test/integration/mocha/requests/integrationNearestRequest.js create mode 100644 test/integration/mocha/server/integrationServerManager.js create mode 100644 test/integration/mocha/service/integrationServiceProcess.js delete mode 100644 test/integration/mocha/topology/integrationTopologyManager.js delete mode 100644 test/integration/resources/topology/correctDBTopology.json delete mode 100644 test/integration/resources/topology/correctOSMTopology.json delete mode 100644 test/integration/resources/topology/incorrectDBTopology.json delete mode 100644 test/integration/resources/topology/incorrectOSMTopology.json delete mode 100644 test/unit/mocha/config/resources/corse.resource create mode 100644 test/unit/mocha/service/testsServiceAdministered.js diff --git a/docker/route-graph-generator b/docker/route-graph-generator index c9b526a..d197411 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit c9b526a1cb4d3972ecbdafac292c8e0c4cc0b242 +Subproject commit d19741120b915d1cf034e099fe5ccad1cbb63e0c diff --git a/src/js/apis/apisManager.js b/src/js/apis/apisManager.js index 5cbf799..dee9b31 100644 --- a/src/js/apis/apisManager.js +++ b/src/js/apis/apisManager.js @@ -14,16 +14,14 @@ module.exports = class apisManager { * * @function * @name constructor - * @description Constructeur de la classe resourceManager + * @description Constructeur de la classe apisManager + * @param {string} apisDirectory - Dossier qui contient les APIs. Par défaut, il s'agit du dossier de ce projet contenant des APIs * */ - constructor() { + constructor(apisDirectory = "../apis/") { // Dossier contenant les APIs du projet - this._apisDirectory = "../apis/"; - - // Prefix utilisé pour chaque route des APIS - this._prefix = ""; + this._apisDirectory = apisDirectory; // Liste des routes chargées par l'app Express this._listOfRoutes = new Array(); @@ -33,50 +31,6 @@ module.exports = class apisManager { } - /** - * - * @function - * @name get apisDirectory - * @description Récupérer le chemin du dossier qui contient les APIs - * - */ - get apisDirectory () { - return this._apisDirectory; - } - - /** - * - * @function - * @name get prefix - * @description Récupérer le prefix utilisé par ce manager - * - */ - get prefix () { - return this._prefix; - } - - /** - * - * @function - * @name get listOfRoutes - * @description Récupérer la liste des routes disponibles via ce manager - * - */ - get listOfRoutes () { - return this._listOfRoutes; - } - - /** - * - * @function - * @name get apisCatalog - * @description Récupérer la liste des apis disponibles via ce manager - * - */ - get apisCatalog () { - return this._apisCatalog; - } - /** * * @function @@ -121,6 +75,7 @@ module.exports = class apisManager { availableAPI= fs.statSync(tmpPathName).isDirectory() } catch(error) { LOGGER.error("Mauvaise configuration: 'name' ne peut être évalué."); + LOGGER.error(error); return false; } @@ -180,95 +135,6 @@ module.exports = class apisManager { } - /** - * - * @function - * @name loadApiDirectory - * @description Fonction utilisée pour charger l'ensemble des APIs disponibles dans un dossier. - * @param {express} app - Objet créé par ExpressJS représentant l'application - * - */ - - loadApiDirectory (app) { - - LOGGER.info("Chargement des APIS..."); - - // Soit _apisDirectory est un chemin absolu qui pointe vers un autre repo d'APIs, soit c'est celui du repo officiel de Road2 (usage de __dirname) - let APIsDirectory = path.resolve(__dirname, this._apisDirectory); - - // on lit le contenu du dossier - let APIsDirectoryTable = new Array(); - - try { - APIsDirectoryTable = fs.readdirSync(APIsDirectory); - } catch (error) { - LOGGER.error("Le dossier n'est pas lisible: " + APIsDirectory); - LOGGER.error(error); - return false; - } - - // S'il est vide ce n'est pas normal - if (APIsDirectoryTable.length === 0) { - LOGGER.error("Le dossier des apis est vide."); - return false; - } - - // Pour chaque sous-dossier on a potentiellement une api - for (let i = 0; i < APIsDirectoryTable.length; i++) { - - let apiName = APIsDirectoryTable[i]; - - let APIDirectory = APIsDirectory + "/" + apiName; - - if (fs.statSync(APIDirectory).isDirectory()) { - // c'est un dossier qui contient potentiellement une API - - LOGGER.info("Nouvelle API: " + apiName); - - let APIDirectoryTable = fs.readdirSync(APIDirectory); - - if (APIDirectoryTable.length === 0) { - LOGGER.error("Le dossier de l'api est vide."); - return false; - } - - for (let j = 0; j < APIDirectoryTable.length; j++) { - - let apiVersion = APIDirectoryTable[j]; - - let APIDirectoryVersion = APIDirectory + "/" + apiVersion; - - if (fs.statSync(APIDirectoryVersion).isDirectory()) { - // c'est un dossier qui contient potentiellement une version de l'API - - LOGGER.info("Nouvelle version: " + apiVersion); - - let configuration = { - name: apiName, - version: apiVersion - }; - - if (!this.loadApiConfiguration(app, configuration)) { - LOGGER.error("Impossible de charger l'API " + apiName + "/" + apiVersion); - } - - } else { - // Si ce n'est pas un dossier, on ne fait rien - } - - } - - } else { - // Si ce n'est pas un dossier, on ne fait rien - } - - } - - LOGGER.info("APIS chargees."); - return true; - - } - /** * * @function @@ -293,16 +159,10 @@ module.exports = class apisManager { // Gestion du router // ------------------ - let route; - - if (this._prefix !== "") { - route = "/" + this._prefix + "/" + apiName + "/" + apiVersion; - } else { - route = "/"+ apiName + "/" + apiVersion; - } + let route = "/"+ apiName + "/" + apiVersion; // On vérifie que la route n'existe pas déjà - if (this.verifyRouteExistanceById(route)) { + if (this._verifyRouteExistanceById(route)) { // Si elle existe c'est un vrai problème donc on arrête le chargement LOGGER.error("La route " + route + " existe deja. L'api n'est donc pas chargee."); return false; @@ -361,14 +221,14 @@ module.exports = class apisManager { /** * * @function - * @name verifyRouteExistanceById + * @name _verifyRouteExistanceById * @description Fonction utilisée pour vérifier que la route n'est pas déjà chargée * @param {string} route - Route à tester * @return {boolean} true si la route est déjà présente et false sinon * */ - verifyRouteExistanceById (route) { + _verifyRouteExistanceById (route) { for (let i = 0; i < this._listOfRoutes.length; i++) { if (this._listOfRoutes[i] === route) { diff --git a/src/js/base/baseManager.js b/src/js/base/baseManager.js index 0866f49..bbe8201 100644 --- a/src/js/base/baseManager.js +++ b/src/js/base/baseManager.js @@ -37,28 +37,6 @@ module.exports = class baseManager { } - /** - * - * @function - * @name get loadedBaseConfiguration - * @description Récupérer la liste des configurations déjà chargées par ce manager - * - */ - get loadedBaseConfiguration() { - return this._loadedBaseConfiguration; - } - - /** - * - * @function - * @name get baseCatalog - * @description Récupérer le catalogue des bases - * - */ - get baseCatalog() { - return this._baseCatalog; - } - /** * * @function diff --git a/src/js/geometry/line.js b/src/js/geometry/line.js index 024154b..8fe498c 100644 --- a/src/js/geometry/line.js +++ b/src/js/geometry/line.js @@ -33,7 +33,7 @@ module.exports = class Line extends Geometry { super("line", projection); - // Géométrie de la polyline + // Géométrie de la Line this._geom = geom; // Format de géométrie (geojson, polyline...) diff --git a/src/js/parameters/boolParameter.js b/src/js/parameters/boolParameter.js index a1a710b..d8b5ace 100644 --- a/src/js/parameters/boolParameter.js +++ b/src/js/parameters/boolParameter.js @@ -29,7 +29,7 @@ module.exports = class BoolParameter extends ResourceParameter { */ constructor(parameter) { - // id + // Paramètre de service super(parameter); // defaultValueContent diff --git a/src/js/parameters/constraintParameter.js b/src/js/parameters/constraintParameter.js index f31070c..dd959a2 100644 --- a/src/js/parameters/constraintParameter.js +++ b/src/js/parameters/constraintParameter.js @@ -31,7 +31,7 @@ module.exports = class ConstraintParameter extends ResourceParameter { */ constructor(parameter) { - // id + // Paramètre de service super(parameter); // defaultValueContent diff --git a/src/js/parameters/enumParameter.js b/src/js/parameters/enumParameter.js index 2228da3..756f789 100644 --- a/src/js/parameters/enumParameter.js +++ b/src/js/parameters/enumParameter.js @@ -29,7 +29,7 @@ module.exports = class EnumParameter extends ResourceParameter { */ constructor(parameter) { - // id + // Paramètre de service super(parameter); // defaultValueContent diff --git a/src/js/parameters/floatParameter.js b/src/js/parameters/floatParameter.js index 26ec6f6..93dc5ad 100644 --- a/src/js/parameters/floatParameter.js +++ b/src/js/parameters/floatParameter.js @@ -30,7 +30,7 @@ module.exports = class FloatParameter extends ResourceParameter { */ constructor(parameter) { - // id + // Paramètre de service super(parameter); // defaultValueContent diff --git a/src/js/parameters/intParameter.js b/src/js/parameters/intParameter.js index a9f4283..271038a 100644 --- a/src/js/parameters/intParameter.js +++ b/src/js/parameters/intParameter.js @@ -29,7 +29,7 @@ module.exports = class IntParameter extends ResourceParameter { */ constructor(parameter) { - // id + // Paramètre de service super(parameter); // defaultValueContent diff --git a/src/js/parameters/pointParameter.js b/src/js/parameters/pointParameter.js index de1f7cd..57be1fd 100644 --- a/src/js/parameters/pointParameter.js +++ b/src/js/parameters/pointParameter.js @@ -30,7 +30,7 @@ module.exports = class PointParameter extends ResourceParameter { */ constructor(parameter) { - // id + // Paramètre de service super(parameter); // Bbox diff --git a/src/js/parameters/resourceParameter.js b/src/js/parameters/resourceParameter.js index c961feb..09334e3 100644 --- a/src/js/parameters/resourceParameter.js +++ b/src/js/parameters/resourceParameter.js @@ -23,12 +23,12 @@ module.exports = class ResourceParameter { * @function * @name constructor * @description Constructeur de la classe ResourceParameter - * @param {object} parameter - Référence au paramètre de service + * @param {Parameter} parameter - Référence au paramètre de service, instance de la classe Parameter * */ constructor(parameter) { - // id + // paramètre du service this._serviceParameter = parameter; } @@ -49,7 +49,7 @@ module.exports = class ResourceParameter { * @function * @name load * @description Charger la configuration - * @param {string} parameterConf - Configuration d'un paramètre + * @param {object} parameterConf - Configuration d'un paramètre * @return {boolean} * */ @@ -115,16 +115,12 @@ module.exports = class ResourceParameter { userTable = userValue.split("|"); } else { - //TODO: ce n'est pas censé arriver, que fait-on ? - return errorManager.createErrorMessage(""); + return errorManager.createErrorMessage("style non reconnu"); } } else { - // on peut avoir simplement un float - // TODO: vérification du float ? - LOGGER.debug("user parameter is NOT a string"); - userTable = userValue; + return errorManager.createErrorMessage("user parameter is NOT a string"); } @@ -167,7 +163,9 @@ module.exports = class ResourceParameter { LOGGER.debug("specificCheck()"); LOGGER.debug("user value : " + userValue); - LOGGER.debug("options: " + options.toString()); + if (options) { + LOGGER.debug("options: " + options.toString()); + } return errorManager.createErrorMessage(""); } @@ -230,7 +228,9 @@ module.exports = class ResourceParameter { LOGGER.debug("specificConversion()"); LOGGER.debug("user value : " + userValue); - LOGGER.debug("options: " + options.toString()); + if (options) { + LOGGER.debug("options: " + options.toString()); + } return null; } diff --git a/src/js/requests/isochroneRequest.js b/src/js/requests/isochroneRequest.js index 343ef21..62e248b 100644 --- a/src/js/requests/isochroneRequest.js +++ b/src/js/requests/isochroneRequest.js @@ -9,9 +9,11 @@ const Request = require('./request'); * @description Classe modélisant une requête d'isochrone. * Chaque requête reçue par le service doit être transformée en requête de cette forme * pour le proxy +* */ module.exports = class isochroneRequest extends Request { + /** * * @function @@ -29,31 +31,29 @@ module.exports = class isochroneRequest extends Request { * @param {string} distanceUnit - Unité de distance utilisée pour le calcul. * */ - constructor( - resource, - point, - costType, - costValue, - profile, - direction, - askedProjection, - geometryFormat, - timeUnit, - distanceUnit - ) { + + constructor(resource, point, costType, costValue, profile, direction, askedProjection, geometryFormat, timeUnit, distanceUnit) { // Constructeur parent super("isochrone", resource, "isochroneRequest"); - /* Initialisation du reste des paramètres. */ + // Initialisation du reste des paramètres this._point = point; + this._costType = costType; + this._costValue = costValue; + this._profile = profile; + this._direction = direction; + this._askedProjection = askedProjection; + this._geometryFormat = geometryFormat; + this._timeUnit = timeUnit; + this._distanceUnit = distanceUnit; // Gestion des contraintes diff --git a/src/js/requests/nearestRequest.js b/src/js/requests/nearestRequest.js index 63bcc00..b640d83 100644 --- a/src/js/requests/nearestRequest.js +++ b/src/js/requests/nearestRequest.js @@ -24,16 +24,16 @@ module.exports = class nearestRequest extends Request { * @param {integer} number - Type du coût. * */ - constructor( - resource, - coordinates - ) { + + constructor(resource, coordinates) { // Constructeur parent super("nearest", resource, "nearestRequest"); - // Initialisation du reste des paramètres. + // Coordonnées du point fourni this._coordinates = coordinates; + + // Nombre de points attendus en retour this._number = 1; } diff --git a/src/js/requests/request.js b/src/js/requests/request.js index 75f6360..107952f 100644 --- a/src/js/requests/request.js +++ b/src/js/requests/request.js @@ -30,7 +30,7 @@ module.exports = class Request { // Ressource concernée this._resource = resource; - // Type de la requête + // Type de la requête (ne doit pas être modifié) this._type = type; } @@ -92,19 +92,4 @@ module.exports = class Request { return this._type; } - /** - * - * @function - * @name set type - * @description Attribuer le type de la requête - * @param {string} ty - Type de la requête - * - */ - set type (ty) { - this._type = ty; - } - - - - } diff --git a/src/js/requests/routeRequest.js b/src/js/requests/routeRequest.js index b38372a..c651ff9 100644 --- a/src/js/requests/routeRequest.js +++ b/src/js/requests/routeRequest.js @@ -56,7 +56,7 @@ module.exports = class routeRequest extends Request { // geometryFormat // type des géométries demandé - this._geometryFormat = "geojson" + this._geometryFormat = "geojson"; // bbox this._bbox = true; @@ -268,6 +268,17 @@ module.exports = class routeRequest extends Request { return this._waysAttributes; } + /** + * + * @function + * @name set waysAttributes + * @description Attribuer la liste des attributs disponibles pour les voies empruntées. + * + */ + set waysAttributes (wa) { + this._waysAttributes = wa; + } + /** * * @function diff --git a/src/js/server/serverManager.js b/src/js/server/serverManager.js index 964d9b0..7139a76 100644 --- a/src/js/server/serverManager.js +++ b/src/js/server/serverManager.js @@ -12,7 +12,7 @@ const LOGGER = log4js.getLogger("SERVERMANAGER"); * * @class * @name serverManager -* @description Gestionnaire des serveurs disponible sur un service +* @description Gestionnaire des serveurs disponibles sur un service ou un administrateur * */ @@ -53,7 +53,7 @@ module.exports = class serverManager { * @param {object} config - Configuration à vérifier * */ - checkServerConfiguration(config) { + checkServerConfiguration(config) { if (!config) { LOGGER.error("Aucune configuration n'a ete fournie"); @@ -316,15 +316,16 @@ module.exports = class serverManager { LOGGER.info("Arret de l'ensemble des serveurs."); if (this._loadedServerId.length === 0) { - LOGGER.warn("Aucun serveur n'est disponible."); + LOGGER.warn("Aucun serveur n'est disponible (id)."); return true; } try { assert.deepStrictEqual(this._serverCatalog, {}); - } catch (err) { - LOGGER.warn("Aucun serveur n'est disponible."); + LOGGER.warn("Aucun serveur n'est disponible (catalog)."); return true; + } catch (err) { + // On continue } for (let serverId in this._serverCatalog) { diff --git a/src/js/service/serviceAdministered.js b/src/js/service/serviceAdministered.js index fdeac44..4fea566 100644 --- a/src/js/service/serviceAdministered.js +++ b/src/js/service/serviceAdministered.js @@ -4,7 +4,7 @@ * * @class * @name ServiceAdministered -* @description Classe modélisant une service administré par l'administrateur. +* @description Classe modélisant une service administré par l'administrateur. C'est une classe mère dérivée par serviceProcess * @param {string} id - Identifiant du service administré * @param {string} type - Type de service administré * @@ -18,6 +18,8 @@ module.exports = class ServiceAdministered { * @function * @name constructor * @description Constructeur de la classe ServiceAdministered + * @param {string} id - id du service + * @param {string} type - Type du service, parmi 'newProcess' pour le moment * */ constructor(id, type) { diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js index 64d4e79..369bc20 100644 --- a/src/js/service/serviceProcess.js +++ b/src/js/service/serviceProcess.js @@ -36,34 +36,9 @@ module.exports = class ServiceProcess extends ServiceAdministered { // Emplacement de la configuration this._configurationLocation = location; - // Stockage de la configuration - this._configuration = {}; - // Instance de childProcess quand le processus est lancé - this._serviceAdministered; - - } + this._serviceAdministered = {}; - /** - * - * @function - * @name get configurationLocation - * @description Récupérer la configurationLocation du service - * - */ - get configurationLocation () { - return this._configurationLocation; - } - - /** - * - * @function - * @name get configuration - * @description Récupérer la configuration du service - * - */ - get configuration () { - return this._configuration; } /** diff --git a/test/functional/readme.md b/test/functional/readme.md index 7cbed9a..9f47973 100644 --- a/test/functional/readme.md +++ b/test/functional/readme.md @@ -4,30 +4,24 @@ Cucumber sera utilisé afin de tester les APIs de Road2, et cela, dans leurs asp ## Utilisation de Cucumber -Il y a plusieurs features cucumber pour effectuer les tests fonctionnels. On retrouve des features pour tester les requêtes qui peuvent être envoyées sur le serveur. Et on retrouve une feature pour tester les différentes configuration que l'on peut fournir au serveur afin de diffuser les services. +Il y a plusieurs features cucumber pour effectuer les tests fonctionnels. On retrouve des features pour tester les requêtes qui peuvent être envoyées sur le serveur. Et on retrouve des features pour tester les différentes configurations que l'on peut fournir au serveur afin de diffuser les services. ### Request -La feature `requestTest.feature` permet de tester les fonctionnalités principales. Elle a l'avantage d'être accessible dès que l'image docker a été construite. La seconde `requestComplementTest.feature` permet de tester plus de fonctionnalités. Pour que cette feature soit testable, il est nécessaire de construire une ressource OSRM et une ressource PGR sur l'île-de-France. C'est pour cette raison que ces features sont séparées. -Enfin, la troisième `requestDataTest.feature` se concentre sur les tests qui impliquent d'avoir une connaissance plus fine de la donnée. Les résultats dépendront des points de départ, d'arrivée et de la présence de pont par exemple. +Les features `request/cucumber/features/req*.feature` permettent de tester les fonctionnalités accessibles via des requêtes. Pour fonctionner, il est nécessaire d'avoir généré des données pour chaque moteur sur l'île-de-France. -Afin de lancer les tests Cucumber, on suivra la procédure suivante: +Afin de lancer ces tests, on suivra la procédure suivante: +- générer des données pour chaque moteur sur l'île-de-France - lancer le serveur Road2 via docker-compose -- exécuter la commande `npm run rtest` via docker-compose (pour tester les fonctionnalités principales de l'api simple 1.0.0) -- exécuter la commande `npm run artest` via docker-compose (pour tester l'api admin 1.0.0) -- générer une ressource OSRM et une ressource PGR sur l'île-de-France via docker-compose -- exécuter la commande `npm run crtest` via docker-compose -- exécuter la commande `npm run drtest` via docker-compose +- exécuter la commande `npm run rtest` via docker-compose. ### Configuration -La feature `configurationTest.feature` permet de tester différentes configuration. Elle a l'avantage d'être accessible dès que l'image docker a été construite. La seconde `configurationComplementTest.feature` permet de tester plus de fonctionnalités. Pour que cette feature soit testable, il est nécessaire de construire une ressource PGR sur l'île-de-France. C'est pour cette raison que ces features sont séparées. +Les features `configuration/cucumber/features/conf*.feature` permettent de tester les fonctionnalités liées au chargement d'une configuration de Road2. -Afin de lancer les tests Cucumber, on suivra la procédure suivante: +Afin de lancer ces tests, on suivra la procédure suivante: - lancer le serveur Road2 via docker-compose -- exécuter la commande `npm run ctest` via docker-compose -- générer une ressource PGR sur l'île-de-France via docker-compose -- exécuter la commande `npm run ccftest` via docker-compose +- exécuter la commande `npm run ctest` via docker-compose. diff --git a/test/integration/mocha/apis/integrationApiManager.js b/test/integration/mocha/apis/integrationApiManager.js index 3df128f..ed318c0 100644 --- a/test/integration/mocha/apis/integrationApiManager.js +++ b/test/integration/mocha/apis/integrationApiManager.js @@ -11,48 +11,65 @@ describe('Test de la classe ApisManager', function() { logManager.manageLogs(); }); - let apisManager = new ApisManager(); - let referenceApp = express(); - let app = express(); - let referenceApi = new Api("simple","1.0.0","/home/docker/app/test/integration/mocha/config/apis/simple/1.0.0/index.js"); - referenceApi.initFile = "/home/docker/app/test/integration/mocha/config/apis/simple/1.0.0/init.js"; - referenceApi.updateFile = "/home/docker/app/test/integration/mocha/config/apis/simple/1.0.0/update.js"; - referenceApi.initialize("/simple/1.0.0", referenceApp); + describe('Test du constructeur et des attributs', function() { - describe('Test du constructeur et des getters', function() { + let apisManager = new ApisManager(); + + it('Get apisDirectory', function() { + assert.deepEqual(apisManager._apisDirectory, "../apis/"); + }); it('Get listOfRoutes', function() { - assert.deepEqual(apisManager.listOfRoutes, new Array()); + assert.deepEqual(apisManager._listOfRoutes, new Array()); }); it('Get apisCatalog', function() { - assert.deepEqual(apisManager.apisCatalog, {}); + assert.deepEqual(apisManager._apisCatalog, {}); }); }); - describe('Test de la fonction loadApiDirectory()', function() { + describe('Test de la fonction checkApiConfiguration()', function() { - it('loadApiDirectory() avec les bons parametres', function() { - assert.equal(apisManager.loadApiDirectory(app, "../../../test/integration/mocha/config/apis/", ""), true); + let apisManager = new ApisManager("../../../test/integration/mocha/config/apis/"); + + it('checkApiConfiguration() avec les bons parametres', function() { + let configuration = {"name" : "simple","version" : "1.0.0"}; + assert.equal(apisManager.checkApiConfiguration(configuration), true); + }); + + it('checkApiConfiguration() avec des mauvais parametres', function() { + let configuration = {"nam" : "todo","versio" : "2.0.0"}; + assert.equal(apisManager.checkApiConfiguration(configuration), false); }); }); - describe('Test de la fonction verifyRouteExistanceById()', function() { + describe('Test de la fonction loadApiConfiguration()', function() { - it('verifyRouteExistanceById() avec une route qui existe', function() { - assert.equal(apisManager.verifyRouteExistanceById("/simple/1.0.0"), true); - }); + let apisManager = new ApisManager("../../../test/integration/mocha/config/apis/"); + let app = express(); + let configuration = {"name" : "simple","version" : "1.0.0"}; - it('verifyRouteExistanceById() avec une route qui n\'existe pas', function() { - assert.equal(apisManager.verifyRouteExistanceById("/test/1.0.3"), false); + it('loadApiConfiguration() avec les bons parametres', function() { + assert.equal(apisManager.loadApiConfiguration(app, configuration), true); }); }); describe('Test de la fonction getApi()', function() { + let apisManager = new ApisManager("../../../test/integration/mocha/config/apis/"); + let app = express(); + let configuration = {"name" : "simple","version" : "1.0.0"}; + apisManager.loadApiConfiguration(app, configuration); + let referenceApp = express(); + let referenceApi = new Api("simple","1.0.0","/home/docker/app/test/integration/mocha/config/apis/simple/1.0.0/index.js"); + referenceApi.initFile = "/home/docker/app/test/integration/mocha/config/apis/simple/1.0.0/init.js"; + referenceApi.updateFile = "/home/docker/app/test/integration/mocha/config/apis/simple/1.0.0/update.js"; + referenceApi.initialize("/simple/1.0.0", referenceApp); + + it('getApi()', function() { assert.deepEqual(apisManager.getApi("simple", "1.0.0"), referenceApi); }); diff --git a/test/integration/mocha/base/integrationBaseManager.js b/test/integration/mocha/base/integrationBaseManager.js index 53fcd01..fd34134 100644 --- a/test/integration/mocha/base/integrationBaseManager.js +++ b/test/integration/mocha/base/integrationBaseManager.js @@ -1,8 +1,7 @@ const assert = require('assert'); -const Base = require('../../../../src/js/base/base'); const BaseManager = require('../../../../src/js/base/baseManager'); -const logManager = require('../../../unit/mocha/logManager'); -const fs = require('fs'); +const logManager = require('../logManager'); +const path = require('path'); describe('Test de la classe BaseManager', function() { @@ -11,24 +10,27 @@ describe('Test de la classe BaseManager', function() { logManager.manageLogs(); }); - let configuration = "/home/docker/app/test/integration/mocha/config/dbs/db_config_test.json"; - let referenceBase = new Base(JSON.parse(fs.readFileSync(configuration))); + let configuration = path.resolve(__dirname, "../config/dbs/db_config_test.json"); let baseManager = new BaseManager(); - describe('Test du constructeur et des getters/setters', function() { + describe('Test du constructeur et des attributs', function() { it('Get loadedBaseConfiguration', function() { - assert.deepEqual(baseManager.loadedBaseConfiguration, new Array()); + assert.deepEqual(baseManager._loadedBaseConfiguration, new Array()); + }); + + it('Get checkedBaseConfiguration', function() { + assert.deepEqual(baseManager._checkedBaseConfiguration, new Array()); }); it('Get baseCatalog', function() { - assert.deepEqual(baseManager.baseCatalog, {}); + assert.deepEqual(baseManager._baseCatalog, {}); }); }); - describe('Verifications des configurations', function() { + describe('Cycle de vérification des configurations', function() { it('checkBaseConfiguration()', async function() { let response = await baseManager.checkBaseConfiguration(configuration); @@ -40,15 +42,27 @@ describe('Test de la classe BaseManager', function() { assert.deepEqual(baseManager._checkedBaseConfiguration, [configuration]); }); + it('flushCheckedBaseConfiguration()', function() { + baseManager.saveCheckedBaseConfiguration(configuration); + baseManager.flushCheckedBaseConfiguration(); + assert.deepEqual(baseManager._checkedBaseConfiguration, new Array()); + }); + }); - describe('Creation d\'une base', function() { + describe('Chargement des configurations', function() { it('loadBaseConfiguration()', function() { - let newBase = baseManager.loadBaseConfiguration(configuration); - // TODO: comprendre pourquoi le assert qui suit ne marche pas - // assert.deepEqual(baseManager.baseCatalog[configuration], referenceBase); - assert.equal(newBase.connected, false); + assert.equal(baseManager.loadBaseConfiguration(configuration), true); + }); + + }); + + describe('Récupération d\'une base', function() { + + it('getBase()', function() { + let base = baseManager.getBase(configuration); + assert.deepEqual(base.connected, false); }); }); diff --git a/test/integration/mocha/config/log4js.json b/test/integration/mocha/config/log4js.json index c8dae0d..2bfbcf1 100644 --- a/test/integration/mocha/config/log4js.json +++ b/test/integration/mocha/config/log4js.json @@ -4,11 +4,11 @@ "console": { "type": "console", "layout": {"type": "pattern", "pattern": "%[[%d] [%p] %c %z -%] %m"} } }, "categories": { - "default": { "appenders": ["console"], "level": "info" } + "default": { "appenders": ["console"], "level": "debug" } } }, "httpConf": { - "level": "info", + "level": "debug", "format": ":remote-addr - :method :url HTTP/:http-version :status :content-length :referrer :user-agent" } } diff --git a/test/integration/mocha/config/road2.json b/test/integration/mocha/config/road2.json new file mode 100644 index 0000000..147ad96 --- /dev/null +++ b/test/integration/mocha/config/road2.json @@ -0,0 +1,27 @@ +{ + "administration":{ + "api" : { + "name" : "admin", + "version" : "1.0.0" + }, + "services": [ + { + "id": "main", + "configuration": "./service.json", + "onStart": "true", + "creationType": "newProcess" + } + ], + "network" : { + "server" : { + "id": "administrator", + "https": "false", + "host": "0.0.0.0", + "port": "8078" + } + }, + "logs": { + "configuration": "./log4js.json" + } + } +} diff --git a/test/unit/mocha/config/road2.json b/test/integration/mocha/config/service.json similarity index 50% rename from test/unit/mocha/config/road2.json rename to test/integration/mocha/config/service.json index 9cfa557..fb56ec3 100644 --- a/test/unit/mocha/config/road2.json +++ b/test/integration/mocha/config/service.json @@ -1,16 +1,16 @@ { "application": { - "name": "Road2-test", - "title": "Tests sur le service de calcul d'itinéraire", - "description": "Ce service permet de calculer des itinéraires sur les données du Géoportail.", - "url": "https://wxs.ign.fr/#KEY/geoportail/itineraire", + "name": "Road2", + "title": "Service de calcul d'itinéraire", + "description": "Ce service permet de calculer des itinéraires", + "url": "https://localhost/", "provider": { "name": "IGN", "site": "www.ign.fr", "mail": "sav@ign.fr" }, "logs": { - "configuration": "/home/docker/app/test/unit/mocha/config/log4js.json" + "configuration": "./log4js-service.json" }, "operations":{ "directory": "/home/docker/app/src/resources/operations", @@ -20,21 +20,35 @@ }, "resources": { "directories": [ - "/home/docker/app/test/unit/mocha/config/resources/" + "/home/docker/data/resources/" + ] + }, + "sources": { + "directories": [ + "/home/docker/data/sources/" ] }, "network": { "servers": [ { - "id": "test", + "id": "internalServer", "https": "false", - "host": "0.0.0.1", - "port": "8080" + "host": "0.0.0.0", + "port": "8091" } - ] - }, + ], + "cors": { + "configuration": "./cors.json" + } + }, "projections": { - "directory": "/home/docker/app/test/unit/mocha/config/projections/" - } + "directory": "./projections/" + }, + "apis": [ + { + "name" : "simple", + "version" : "1.0.0" + } + ] } } diff --git a/test/integration/mocha/geometry/integrationLine.js b/test/integration/mocha/geometry/integrationLine.js index 45bf2d9..8724b65 100644 --- a/test/integration/mocha/geometry/integrationLine.js +++ b/test/integration/mocha/geometry/integrationLine.js @@ -1,7 +1,7 @@ const assert = require('assert'); const Line = require('../../../../src/js/geometry/line'); const proj4 = require('proj4'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); describe('Test de la classe Line', function() { @@ -40,7 +40,7 @@ describe('Test de la classe Line', function() { describe('Test du constructeur et des getters/setters', function() { it('Get type', function() { - assert.equal(line.type, "polyline"); + assert.equal(line.type, "line"); }); it('Get projection', function() { diff --git a/test/integration/mocha/operations/integrationOperation.js b/test/integration/mocha/operations/integrationOperation.js index fdf3a10..616a626 100644 --- a/test/integration/mocha/operations/integrationOperation.js +++ b/test/integration/mocha/operations/integrationOperation.js @@ -1,7 +1,7 @@ const assert = require('assert'); const Operation = require('../../../../src/js/operations/operation'); const Parameter = require('../../../../src/js/parameters/parameter'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); describe('Test de la classe Operation', function() { diff --git a/test/integration/mocha/operations/integrationResourceOperation.js b/test/integration/mocha/operations/integrationResourceOperation.js index a6decad..736463c 100644 --- a/test/integration/mocha/operations/integrationResourceOperation.js +++ b/test/integration/mocha/operations/integrationResourceOperation.js @@ -1,9 +1,8 @@ const assert = require('assert'); const ResourceOperation = require('../../../../src/js/operations/resourceOperation'); -const Parameter = require('../../../../src/js/parameters/parameter'); const ResourceParameter = require('../../../../src/js/parameters/resourceParameter'); const logManager = require('../logManager'); -const sinon = require('sinon'); +const Parameter = require('../../../../src/js/parameters/parameter'); describe('Test de la classe ResourceOperation', function() { @@ -12,17 +11,11 @@ describe('Test de la classe ResourceOperation', function() { logManager.manageLogs(); }); - // Pour ne pas dépendre de la classe Parameter et resourceParameter - let serviceParameter = sinon.mock(Parameter); - serviceParameter.id = "start"; - - let resourceParameter = sinon.mock(ResourceParameter); - resourceParameter.serviceParameter = serviceParameter; - - let parameters = {}; - parameters["start"] = serviceParameter; - - let resourceOperation = new ResourceOperation("route", parameters); + let serviceParameter = new Parameter("start","point","start","Points de départ","true","false") + let resourceParameter = new ResourceParameter(serviceParameter); + let parameterHash = {}; + parameterHash["start"] = resourceParameter; + let resourceOperation = new ResourceOperation("route", parameterHash); describe('Test du constructeur et des getters', function() { @@ -31,11 +24,19 @@ describe('Test de la classe ResourceOperation', function() { }); it('Get resourceParameters', function() { - assert.deepEqual(resourceOperation.resourceParameters, parameters); + assert.deepEqual(resourceOperation.resourceParameters, parameterHash); + }); + + }); + + describe('Test du constructeur et des getters', function() { + + it('getParameterById() d\'un paramètre existant', function() { + assert.deepEqual(resourceOperation.getParameterById("start"), parameterHash["start"]); }); - it('getParameterById()', function() { - assert.deepEqual(resourceOperation.getParameterById("start"), parameters["start"]); + it('getParameterById() d\'un paramètre non existant', function() { + assert.deepEqual(resourceOperation.getParameterById("end"), {}); }); }); diff --git a/test/integration/mocha/parameters/integrationBoolParameter.js b/test/integration/mocha/parameters/integrationBoolParameter.js new file mode 100644 index 0000000..f60c391 --- /dev/null +++ b/test/integration/mocha/parameters/integrationBoolParameter.js @@ -0,0 +1,82 @@ +const assert = require('assert'); +const Parameter = require('../../../../src/js/parameters/parameter'); +const BoolParameter = require('../../../../src/js/parameters/boolParameter'); +const logManager = require('../logManager'); + +describe('Test de la classe BoolParameter', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let parameterConf = {"id": "bbox", "defaultValueContent": "true"}; + let parameter = new Parameter("bbox","boolean","bbox","bbox","false","true"); + let boolParameter = new BoolParameter(parameter); + + describe('Test du constructeur et des getters', function() { + + it('Get serviceParameter', function() { + assert.equal(boolParameter.serviceParameter.style, "pipeDelimited"); + }); + + it('Get defaultValueContent', function() { + assert.equal(boolParameter.defaultValueContent, null); + }); + + it('Get values', function() { + assert.deepEqual(boolParameter.values, [true, false]); + }); + + }); + + describe('Test du chargement', function() { + + it('load()', function() { + assert.equal(boolParameter.load(parameterConf), true); + }); + + }); + + describe('Test des vérifications', function() { + + it('check() avec bonne valeur', function() { + assert.equal(boolParameter.check("true").code, "ok"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(boolParameter.check("toto").code, "error"); + }); + + it('specificCheck() avec bonne valeur', function() { + assert.equal(boolParameter.specificCheck("true").code, "ok"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(boolParameter.specificCheck("toto").code, "error"); + }); + + }); + + describe('Test des conversions', function() { + + it('convertIntoTable() avec des paramètres bons', function() { + assert.equal(boolParameter.convertIntoTable("true|false", [], {}), true); + }); + + it('specificConvertion() avec des bons paramètres', function() { + assert.equal(boolParameter.specificConvertion("true"), true); + }); + + it('specificConvertion() avec des bons paramètres', function() { + assert.equal(boolParameter.specificConvertion("false"), false); + }); + + it('specificConvertion() avec des mauvais paramètres', function() { + assert.equal(boolParameter.specificConvertion("test"), null); + }); + + }); + + +}); diff --git a/test/integration/mocha/parameters/integrationConstraintParameter.js b/test/integration/mocha/parameters/integrationConstraintParameter.js new file mode 100644 index 0000000..a8f044a --- /dev/null +++ b/test/integration/mocha/parameters/integrationConstraintParameter.js @@ -0,0 +1,76 @@ +const assert = require('assert'); +const Parameter = require('../../../../src/js/parameters/parameter'); +const ConstraintParameter = require('../../../../src/js/parameters/constraintParameter'); +const logManager = require('../logManager'); +const Constraint = require('../../../../src/js/constraint/constraint'); + +describe('Test de la classe ConstraintParameter', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let refContraint = new Constraint("banned","waytype","toll","=","autoroute"); + let parameterConf = {"id":"constraints","values":[{"keyType":"name-osrm","key":"waytype","availableConstraintType":["banned"],"availableValues":[{"value":"autoroute","field":"toll"},{"value":"tunnel","field":"tunnel"},{"value":"pont","field":"bridge"}]}]}; + let parameter = new Parameter("resource","enumeration","resource","resource","true","false"); + let constraintParameter = new ConstraintParameter(parameter); + + describe('Test du constructeur et des getters', function() { + + it('Get serviceParameter', function() { + assert.equal(constraintParameter.serviceParameter.style, "pipeDelimited"); + }); + + it('Get defaultValueContent', function() { + assert.deepEqual(constraintParameter.defaultValueContent, {}); + }); + + it('Get values', function() { + assert.deepEqual(constraintParameter.values, new Array()); + }); + + }); + + describe('Test du chargement', function() { + + it('load()', function() { + assert.equal(constraintParameter.load(parameterConf), true); + }); + + }); + + describe('Test des vérifications', function() { + + it('check() avec bonne valeur', function() { + assert.equal(constraintParameter.check("{\"constraintType\":\"banned\",\"key\":\"waytype\",\"operator\":\"=\",\"value\":\"autoroute\"}").code, "ok"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(constraintParameter.check("test").code, "error"); + }); + + it('specificCheck() avec bonne valeur', function() { + assert.equal(constraintParameter.specificCheck("{\"constraintType\":\"banned\",\"key\":\"waytype\",\"operator\":\"=\",\"value\":\"autoroute\"}").code, "ok"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(constraintParameter.specificCheck("test").code, "error"); + }); + + }); + + describe('Test des conversions', function() { + + it('convertIntoTable()', function() { + assert.equal(constraintParameter.convertIntoTable("{\"constraintType\":\"banned\",\"key\":\"waytype\",\"operator\":\"=\",\"value\":\"autoroute\"}\|{\"constraintType\":\"banned\",\"key\":\"waytype\",\"operator\":\"=\",\"value\":\"tunnel\"}", [], {}), true); + }); + + it('specificConvertion()', function() { + assert.deepEqual(constraintParameter.specificConvertion("{\"constraintType\":\"banned\",\"key\":\"waytype\",\"operator\":\"=\",\"value\":\"autoroute\"}"), refContraint); + }); + + }); + + +}); diff --git a/test/integration/mocha/parameters/integrationEnumParameter.js b/test/integration/mocha/parameters/integrationEnumParameter.js new file mode 100644 index 0000000..f6dbb7e --- /dev/null +++ b/test/integration/mocha/parameters/integrationEnumParameter.js @@ -0,0 +1,82 @@ +const assert = require('assert'); +const Parameter = require('../../../../src/js/parameters/parameter'); +const EnumParameter = require('../../../../src/js/parameters/enumParameter'); +const logManager = require('../logManager'); + +describe('Test de la classe EnumParameter', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let parameterConf = {"id": "resource","values": ["data","data2"]}; + let parameter = new Parameter("resource","enumeration","resource","resource","true","false"); + let enumParameter = new EnumParameter(parameter); + + describe('Test du constructeur et des getters', function() { + + it('Get serviceParameter', function() { + assert.equal(enumParameter.serviceParameter.style, "pipeDelimited"); + }); + + it('Get defaultValueContent', function() { + assert.equal(enumParameter.defaultValueContent, ""); + }); + + it('Get values', function() { + assert.deepEqual(enumParameter.values, new Array()); + }); + + }); + + describe('Test du chargement', function() { + + it('load()', function() { + assert.equal(enumParameter.load(parameterConf), true); + }); + + }); + + describe('Test des vérifications', function() { + + it('check() avec bonne valeur', function() { + assert.equal(enumParameter.check("data").code, "ok"); + }); + + it('check() avec autre bonne valeur', function() { + assert.equal(enumParameter.check("data2").code, "ok"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(enumParameter.check("test").code, "error"); + }); + + it('specificCheck() avec bonne valeur', function() { + assert.equal(enumParameter.specificCheck("data").code, "ok"); + }); + + it('specificCheck() avec autre bonne valeur', function() { + assert.equal(enumParameter.specificCheck("data2").code, "ok"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(enumParameter.specificCheck("test").code, "error"); + }); + + }); + + describe('Test des conversions', function() { + + it('convertIntoTable()', function() { + assert.equal(enumParameter.convertIntoTable("data|data2", [], {}), true); + }); + + it('specificConvertion()', function() { + assert.equal(enumParameter.specificConvertion("data"), "data"); + }); + + }); + + +}); diff --git a/test/integration/mocha/parameters/integrationFloatParameter.js b/test/integration/mocha/parameters/integrationFloatParameter.js new file mode 100644 index 0000000..922dc6a --- /dev/null +++ b/test/integration/mocha/parameters/integrationFloatParameter.js @@ -0,0 +1,110 @@ +const assert = require('assert'); +const Parameter = require('../../../../src/js/parameters/parameter'); +const FloatParameter = require('../../../../src/js/parameters/floatParameter'); +const logManager = require('../logManager'); + +describe('Test de la classe FloatParameter', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let parameterConf = {"id": "costValue","values": {"min": 100,"max": 20000}}; + let parameter = new Parameter("costValue","float","costValue","costValue","true","false"); + let floatParameter = new FloatParameter(parameter); + + describe('Test du constructeur et des getters', function() { + + it('Get serviceParameter', function() { + assert.equal(floatParameter.serviceParameter.style, "pipeDelimited"); + }); + + it('Get defaultValueContent', function() { + assert.equal(floatParameter.defaultValueContent, 0); + }); + + it('Get values', function() { + assert.deepEqual(floatParameter.values, {min:null,max:null}); + }); + + }); + + describe('Test du chargement', function() { + + it('load()', function() { + assert.equal(floatParameter.load(parameterConf), true); + }); + + it('Get defaultValueContent', function() { + assert.equal(floatParameter.defaultValueContent, 0); + }); + + it('Get values', function() { + assert.deepEqual(floatParameter.values, {min:100,max:20000}); + }); + + }); + + describe('Test des vérifications', function() { + + it('check() avec bonne valeur', function() { + assert.equal(floatParameter.check("200").code, "ok"); + }); + + it('check() avec autre bonne valeur', function() { + assert.equal(floatParameter.check("10000").code, "ok"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(floatParameter.check("test").code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(floatParameter.check(1).code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(floatParameter.check("1").code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(floatParameter.check("300000").code, "error"); + }); + + it('specificCheck() avec bonne valeur', function() { + assert.equal(floatParameter.specificCheck("200").code, "ok"); + }); + + it('specificCheck() avec autre bonne valeur', function() { + assert.equal(floatParameter.specificCheck("10000").code, "ok"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(floatParameter.specificCheck("test").code, "error"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(floatParameter.specificCheck(1).code, "error"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(floatParameter.specificCheck(300000).code, "error"); + }); + + }); + + describe('Test des conversions', function() { + + it('convertIntoTable()', function() { + assert.equal(floatParameter.convertIntoTable("data|data2", [], {}), true); + }); + + it('specificConvertion()', function() { + assert.equal(floatParameter.specificConvertion(1.321), 1.321); + }); + + }); + + +}); diff --git a/test/integration/mocha/parameters/integrationIntParameter.js b/test/integration/mocha/parameters/integrationIntParameter.js new file mode 100644 index 0000000..16ea6be --- /dev/null +++ b/test/integration/mocha/parameters/integrationIntParameter.js @@ -0,0 +1,110 @@ +const assert = require('assert'); +const Parameter = require('../../../../src/js/parameters/parameter'); +const IntParameter = require('../../../../src/js/parameters/intParameter'); +const logManager = require('../logManager'); + +describe('Test de la classe IntParameter', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let parameterConf = {"id": "number", "defaultValueContent": 1, "values": {"min": 1,"max": 10}}; + let parameter = new Parameter("number","integer","number","number","false","true"); + let intParameter = new IntParameter(parameter); + + describe('Test du constructeur et des getters', function() { + + it('Get serviceParameter', function() { + assert.equal(intParameter.serviceParameter.style, "pipeDelimited"); + }); + + it('Get defaultValueContent', function() { + assert.equal(intParameter.defaultValueContent, 0); + }); + + it('Get values', function() { + assert.deepEqual(intParameter.values, {min:null,max:null}); + }); + + }); + + describe('Test du chargement', function() { + + it('load()', function() { + assert.equal(intParameter.load(parameterConf), true); + }); + + it('Get defaultValueContent', function() { + assert.equal(intParameter.defaultValueContent, 1); + }); + + it('Get values', function() { + assert.deepEqual(intParameter.values, {min:1,max:10}); + }); + + }); + + describe('Test des vérifications', function() { + + it('check() avec bonne valeur', function() { + assert.equal(intParameter.check("2").code, "ok"); + }); + + it('check() avec autre bonne valeur', function() { + assert.equal(intParameter.check("10").code, "ok"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(intParameter.check("test").code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(intParameter.check(1).code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(intParameter.check("0").code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(intParameter.check("2000").code, "error"); + }); + + it('specificCheck() avec bonne valeur', function() { + assert.equal(intParameter.specificCheck("2").code, "ok"); + }); + + it('specificCheck() avec autre bonne valeur', function() { + assert.equal(intParameter.specificCheck("10").code, "ok"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(intParameter.specificCheck("test").code, "error"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(intParameter.specificCheck(0).code, "error"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(intParameter.specificCheck(2000).code, "error"); + }); + + }); + + describe('Test des conversions', function() { + + it('convertIntoTable()', function() { + assert.equal(intParameter.convertIntoTable("data|data2", [], {}), true); + }); + + it('specificConvertion()', function() { + assert.equal(intParameter.specificConvertion(1), 1); + }); + + }); + + +}); diff --git a/test/integration/mocha/parameters/integrationPointParameter.js b/test/integration/mocha/parameters/integrationPointParameter.js new file mode 100644 index 0000000..a39f49d --- /dev/null +++ b/test/integration/mocha/parameters/integrationPointParameter.js @@ -0,0 +1,92 @@ +const assert = require('assert'); +const Point = require('../../../../src/js/geometry/point'); +const Parameter = require('../../../../src/js/parameters/parameter'); +const PointParameter = require('../../../../src/js/parameters/pointParameter'); +const logManager = require('../logManager'); + +describe('Test de la classe PointParameter', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let refPoint = new Point(2,48.5,"EPSG:4326"); + let parameterConf = {"id": "point","values": {"bbox": "1.7,48.4,3.3,49.1","projection": "EPSG:4326"}}; + let parameter = new Parameter("start","point","start","start","true","false"); + let pointParameter = new PointParameter(parameter); + + describe('Test du constructeur et des getters', function() { + + it('Get serviceParameter', function() { + assert.equal(pointParameter.serviceParameter.style, "pipeDelimited"); + }); + + it('Get defaultValueContent', function() { + assert.equal(pointParameter.defaultValueContent, ""); + }); + + it('Get values', function() { + assert.deepEqual(pointParameter.values, {bbox:""}); + }); + + }); + + describe('Test du chargement', function() { + + it('load()', function() { + assert.equal(pointParameter.load(parameterConf), true); + }); + + it('Get defaultValueContent', function() { + assert.equal(pointParameter.defaultValueContent, ""); + }); + + it('Get values', function() { + assert.deepEqual(pointParameter.values, {bbox:"1.7,48.4,3.3,49.1"}); + }); + + }); + + describe('Test des vérifications', function() { + + it('check() avec bonne valeur', function() { + assert.equal(pointParameter.check("2.1,48.6","EPSG:4326").code, "ok"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(pointParameter.check("2.1,48","EPSG:4326").code, "error"); + }); + + it('check() avec mauvaise valeur', function() { + assert.equal(pointParameter.check("1.5,48.5","EPSG:4326").code, "error"); + }); + + it('specificCheck() avec bonne valeur', function() { + assert.equal(pointParameter.specificCheck("2.1,48.6","EPSG:4326").code, "ok"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(pointParameter.specificCheck("2.1,48","EPSG:4326").code, "error"); + }); + + it('specificCheck() avec mauvaise valeur', function() { + assert.equal(pointParameter.specificCheck("1.5,48.5","EPSG:4326").code, "error"); + }); + + }); + + describe('Test des conversions', function() { + + it('convertIntoTable()', function() { + assert.equal(pointParameter.convertIntoTable("2,48.5|2,48.6", [], "EPSG:4326"), true); + }); + + it('specificConvertion()', function() { + assert.deepEqual(pointParameter.specificConvertion("2,48.5","EPSG:4326"), refPoint); + }); + + }); + + +}); diff --git a/test/integration/mocha/parameters/integrationResourceParameter.js b/test/integration/mocha/parameters/integrationResourceParameter.js index 0133a60..19cf381 100644 --- a/test/integration/mocha/parameters/integrationResourceParameter.js +++ b/test/integration/mocha/parameters/integrationResourceParameter.js @@ -1,58 +1,85 @@ const assert = require('assert'); +const Parameter = require('../../../../src/js/parameters/parameter'); const ResourceParameter = require('../../../../src/js/parameters/resourceParameter'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); -describe('Test de la classe RouteRequest', function() { +describe('Test de la classe ResourceParameter', function() { before(function() { // runs before all tests in this block logManager.manageLogs(); }); - let resourceParameter = new ResourceParameter({explode: true, min: 0, max: 5, style: "pipeDelimited"}); + let parameter = new Parameter("intermediates","point","intermediates","Points intermédiaires","true","false"); + let resourceParameter = new ResourceParameter(parameter); describe('Test du constructeur et des getters', function() { - it('Get Parameter', function() { + it('Get serviceParameter', function() { assert.equal(resourceParameter.serviceParameter.style, "pipeDelimited"); }); }); - describe('Test de load', function() { + describe('Test du chargement', function() { - it('Load', function() { - assert.equal(resourceParameter.load("toto"), false); + // Dans cette classe, cette fonction renvoit toujours false car elle est réellement implémentée dans les classes filles + it('load()', function() { + assert.equal(resourceParameter.load({"id":"test"}), false); }); }); - describe('Test des check', function() { + describe('Test des vérifications', function() { - it('Check', function() { + // Dans cette classe, cette fonction renvoit toujours une erreur car elle fait appel à specificCheck() qui est réellement implémentée dans les classes filles + it('check() avec options', function() { + assert.equal(resourceParameter.check("toto", {"test":true}).code, "error"); + }); + + it('check() avec options vides', function() { assert.equal(resourceParameter.check("toto", {}).code, "error"); }); - it('Specific check', function() { + it('check() sans options', function() { + assert.equal(resourceParameter.check("toto").code, "error"); + }); + + // Dans cette classe, cette fonction renvoit toujours une erreur car elle est réellement implémentée dans les classes filles + it('specificCheck() avec options', function() { + assert.equal(resourceParameter.specificCheck("toto", {"test":true}).code, "error"); + }); + + it('specificCheck() avec options vides', function() { assert.equal(resourceParameter.specificCheck("toto", {}).code, "error"); }); + it('specificCheck() sans options', function() { + assert.equal(resourceParameter.specificCheck("toto").code, "error"); + }); + }); describe('Test des conversions', function() { - it('Convert into table OK', function() { - assert.equal(resourceParameter.convertIntoTable(["toto", "tata"], [], {}), false); + // Dans cette classe, cette fonction renvoit toujours une erreur car elle fait appel à specificConversion() qui est réellement implémentée dans les classes filles + it('convertIntoTable() avec des paramètres bons', function() { + assert.equal(resourceParameter.convertIntoTable("test", [], {}), false); }); - it('Convert into table OK not exploded', function() { - assert.equal(resourceParameterNotExplode.convertIntoTable("toto|tata", [], {}), true); + // Dans cette classe, cette fonction renvoit toujours une erreur car elle est réellement implémentée dans les classes filles + it('specificConvertion() avec options', function() { + assert.equal(resourceParameter.specificConvertion("toto", {"test":true}), null); }); - it('Specific Convertion', function() { + it('specificConvertion() avec options vides', function() { assert.equal(resourceParameter.specificConvertion("toto", {}), null); }); + it('specificConvertion() sans options', function() { + assert.equal(resourceParameter.specificConvertion("toto"), null); + }); + }); diff --git a/test/integration/mocha/requests/integrationIsochroneRequest.js b/test/integration/mocha/requests/integrationIsochroneRequest.js index 1c004a6..6034fed 100644 --- a/test/integration/mocha/requests/integrationIsochroneRequest.js +++ b/test/integration/mocha/requests/integrationIsochroneRequest.js @@ -1,6 +1,6 @@ const assert = require('assert'); const IsochroneRequest = require('../../../../src/js/requests/isochroneRequest'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); describe('Test de la classe IsochroneRequest', function() { @@ -9,7 +9,7 @@ describe('Test de la classe IsochroneRequest', function() { logManager.manageLogs(); }); - let request = new IsochroneRequest("corse-osm", {lon: 8.732901, lat: 41.928821}, "time", 100, "car", "departure", "EPSG:4326", "geojson", "s", "m"); + let request = new IsochroneRequest("corse-osm", {lon: 8.732901, lat: 41.928821}, "time", 100, "car", "departure", "EPSG:4326", "geojson", "minute", "meter"); describe('Test du constructeur et des getters', function() { @@ -54,11 +54,15 @@ describe('Test de la classe IsochroneRequest', function() { }); it('Get timeUnit', function() { - assert.deepEqual(request.timeUnit, "s"); + assert.deepEqual(request.timeUnit, "minute"); }); it('Get distanceUnit', function() { - assert.deepEqual(request.distanceUnit, "m"); + assert.deepEqual(request.distanceUnit, "meter"); + }); + + it('Get constraints', function() { + assert.deepEqual(request.constraints, new Array()); }); }); @@ -75,9 +79,9 @@ describe('Test de la classe IsochroneRequest', function() { assert.equal(request.resource, "corse-osm-2"); }); - it('Set Type', function() { + it('Set Type ne change rien', function() { request.type = "otherRequest"; - assert.equal(request.type, "otherRequest"); + assert.equal(request.type, "isochroneRequest"); }); it('Set point', function() { @@ -116,13 +120,18 @@ describe('Test de la classe IsochroneRequest', function() { }); it('Set timeUnit', function() { - request.timeUnit = "min"; - assert.deepEqual(request.timeUnit, "min"); + request.timeUnit = "second"; + assert.deepEqual(request.timeUnit, "second"); }); it('Set distanceUnit', function() { - request.distanceUnit = "km"; - assert.deepEqual(request.distanceUnit, "km"); + request.distanceUnit = "kilometer"; + assert.deepEqual(request.distanceUnit, "kilometer"); + }); + + it('Set constraints', function() { + request.constraints = [{"test":true}]; + assert.deepEqual(request.constraints, [{"test":true}]); }); diff --git a/test/integration/mocha/requests/integrationNearestRequest.js b/test/integration/mocha/requests/integrationNearestRequest.js new file mode 100644 index 0000000..d0a6951 --- /dev/null +++ b/test/integration/mocha/requests/integrationNearestRequest.js @@ -0,0 +1,70 @@ +const assert = require('assert'); +const NearestRequest = require('../../../../src/js/requests/nearestRequest'); +const Point = require('../../../../src/js/geometry/point'); +const logManager = require('../logManager'); + +describe('Test de la classe NearestRequest', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let point = new Point(2,48,"EPSG:4326"); + let request = new NearestRequest("data", point); + + describe('Test du constructeur et des getters', function() { + + it('Get Operation', function() { + assert.equal(request.operation, "nearest"); + }); + + it('Get Resource', function() { + assert.equal(request.resource, "data"); + }); + + it('Get Type', function() { + assert.equal(request.type, "nearestRequest"); + }); + + it('Get coordinates', function() { + assert.deepEqual(request.coordinates, point); + }); + + it('Get number', function() { + assert.equal(request.number, 1); + }); + + }); + + describe('Test des setters', function() { + + it('Set Operation', function() { + request.operation = "nearest"; + assert.equal(request.operation, "nearest"); + }); + + it('Set Resource', function() { + request.resource = "data"; + assert.equal(request.resource, "data"); + }); + + it('Set Type ne change rien', function() { + request.type = "otherRequest"; + assert.equal(request.type, "nearestRequest"); + }); + + it('Set Start', function() { + let newPoint = new Point(2.1,48.2,"EPSG:4326"); + request.coordinates = newPoint; + assert.deepEqual(request.coordinates, newPoint); + }); + + it('Set number', function() { + request.number = 2; + assert.deepEqual(request.number, 2); + }); + + }); + +}); diff --git a/test/integration/mocha/requests/integrationRouteRequest.js b/test/integration/mocha/requests/integrationRouteRequest.js index 1de841a..404859d 100644 --- a/test/integration/mocha/requests/integrationRouteRequest.js +++ b/test/integration/mocha/requests/integrationRouteRequest.js @@ -1,6 +1,6 @@ const assert = require('assert'); const RouteRequest = require('../../../../src/js/requests/routeRequest'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); describe('Test de la classe RouteRequest', function() { @@ -49,6 +49,30 @@ describe('Test de la classe RouteRequest', function() { assert.deepEqual(request.intermediates, new Array()); }); + it('Get waysAttributes', function() { + assert.deepEqual(request.waysAttributes, new Array()); + }); + + it('Get geometryFormat', function() { + assert.deepEqual(request.geometryFormat, "geojson"); + }); + + it('Get bbox', function() { + assert.deepEqual(request.bbox, true); + }); + + it('Get timeUnit', function() { + assert.deepEqual(request.timeUnit, "minute"); + }); + + it('Get distanceUnit', function() { + assert.deepEqual(request.distanceUnit, "meter"); + }); + + it('Get constraints', function() { + assert.deepEqual(request.constraints, new Array()); + }); + }); describe('Test des setters', function() { @@ -63,9 +87,9 @@ describe('Test de la classe RouteRequest', function() { assert.equal(request.resource, "corse-osm-2"); }); - it('Set Type', function() { + it('Set Type ne change rien', function() { request.type = "otherRequest"; - assert.equal(request.type, "otherRequest"); + assert.equal(request.type, "routeRequest"); }); it('Set Start', function() { @@ -98,7 +122,48 @@ describe('Test de la classe RouteRequest', function() { assert.deepEqual(request.intermediates, [{lon: 8.732902, lat: 41.953932},{lon: 8.732801, lat: 41.953835}]); }); + it('Set waysAttributes', function() { + request.waysAttributes = ["name"]; + assert.deepEqual(request.waysAttributes, ["name"]); + }); + + it('Set geometryFormat', function() { + request.geometryFormat = "wkt"; + assert.deepEqual(request.geometryFormat, "wkt"); + }); + + it('Set bbox', function() { + request.bbox = false; + assert.deepEqual(request.bbox, false); + }); + + it('Set timeUnit', function() { + request.timeUnit = "second"; + assert.deepEqual(request.timeUnit, "second"); + }); + + it('Set distanceUnit', function() { + request.distanceUnit = "kilometer"; + assert.deepEqual(request.distanceUnit, "kilometer"); + }); + + it('Set constraints', function() { + request.constraints = [{"test":true}] + assert.deepEqual(request.constraints, [{"test":true}]); + }); + }); + describe('Test de isAttributeRequested()', function() { + + it('isAttributeRequested() d\'un attribut demandé', function() { + assert.equal(request.isAttributeRequested("name"), true); + }); + + it('isAttributeRequested() d\'un attribut non demandé', function() { + assert.equal(request.isAttributeRequested("test"), false); + }); + + }); }); diff --git a/test/integration/mocha/resources/integrationOsrmResource.js b/test/integration/mocha/resources/integrationOsrmResource.js index ba8123d..ff9a4ef 100644 --- a/test/integration/mocha/resources/integrationOsrmResource.js +++ b/test/integration/mocha/resources/integrationOsrmResource.js @@ -1,7 +1,7 @@ const assert = require('assert'); const OsrmResource = require('../../../../src/js/resources/osrmResource'); const RouteRequest = require('../../../../src/js/requests/routeRequest'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); const sinon = require('sinon'); diff --git a/test/integration/mocha/resources/integrationResourceManager.js b/test/integration/mocha/resources/integrationResourceManager.js index 6c0f930..d825b7f 100644 --- a/test/integration/mocha/resources/integrationResourceManager.js +++ b/test/integration/mocha/resources/integrationResourceManager.js @@ -1,7 +1,6 @@ const assert = require('assert'); const ResourceManager = require('../../../../src/js/resources/resourceManager'); const SourceManager = require('../../../../src/js/sources/sourceManager'); -const TopologyManager = require('../../../../src/js/topology/topologyManager'); const OperationManager = require('../../../../src/js/operations/operationManager'); const logManager = require('../logManager'); @@ -163,13 +162,11 @@ describe('Test de la classe ResourceManager', function() { let resourceManager = new ResourceManager(); let sourceManager = sinon.mock(SourceManager); - let topologyManager = sinon.mock(TopologyManager); let operationManager = sinon.mock(OperationManager); // Comportements attendus sourceManager.checkSource = sinon.stub().withArgs(resourceConfiguration).returns(true); sourceManager.sourceTopology = new Array(); - topologyManager.checkTopology = sinon.stub().returns(true); operationManager.checkResourceOperationConf = sinon.stub().returns(true); operationManager.getResourceOperationConf = sinon.stub().returns(true); operationManager.createResourceOperation = sinon.stub().returns(true); @@ -185,7 +182,7 @@ describe('Test de la classe ResourceManager', function() { describe('Test de checkResource() et checkResourceOsrm()', function() { it('Avec les bons parametres', function() { - assert.equal(resourceManager.checkResource(resourceConfiguration, sourceManager, operationManager, topologyManager), true); + assert.equal(resourceManager.checkResource(resourceConfiguration, sourceManager, operationManager), true); }); it('checkResource() avec un mauvais id', function() { diff --git a/test/integration/mocha/resources/integrationValhallaResource.js b/test/integration/mocha/resources/integrationValhallaResource.js index b248a59..3685284 100644 --- a/test/integration/mocha/resources/integrationValhallaResource.js +++ b/test/integration/mocha/resources/integrationValhallaResource.js @@ -1,7 +1,7 @@ const assert = require('assert'); const ValhallaResource = require('../../../../src/js/resources/valhallaResource'); const RouteRequest = require('../../../../src/js/requests/routeRequest'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); const sinon = require('sinon'); diff --git a/test/integration/mocha/responses/integrationPortion.js b/test/integration/mocha/responses/integrationPortion.js index 8d9766b..ffd17da 100644 --- a/test/integration/mocha/responses/integrationPortion.js +++ b/test/integration/mocha/responses/integrationPortion.js @@ -1,7 +1,7 @@ const assert = require('assert'); const Portion = require('../../../../src/js/responses/portion'); const Step = require('../../../../src/js/responses/step'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); describe('Test de la classe Portion', function() { diff --git a/test/integration/mocha/server/integrationServerManager.js b/test/integration/mocha/server/integrationServerManager.js new file mode 100644 index 0000000..93963b2 --- /dev/null +++ b/test/integration/mocha/server/integrationServerManager.js @@ -0,0 +1,87 @@ +const assert = require('assert'); +const ServerManager = require('../../../../src/js/server/serverManager'); +const logManager = require('../logManager'); +const path = require('path'); +const fs = require('fs'); +const express = require('express'); + +describe('Test de la classe ServerManager', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let file = path.resolve(__dirname, "../config/road2.json"); + let configuration = JSON.parse(fs.readFileSync(file)); + configuration = configuration.administration.network.server; + + let app = express(); + + let serverManager = new ServerManager(); + + describe('Test du constructeur et des attributs', function() { + + it('Get _loadedServerId', function() { + assert.deepEqual(serverManager._loadedServerId, new Array()); + }); + + it('Get _checkedServerId', function() { + assert.deepEqual(serverManager._checkedServerId, new Array()); + }); + + it('Get _serverCatalog', function() { + assert.deepEqual(serverManager._serverCatalog, {}); + }); + + it('Get _loadedServerDescription', function() { + assert.deepEqual(serverManager._loadedServerDescription, {}); + }); + + it('Get _checkedServerDescription', function() { + assert.deepEqual(serverManager._checkedServerDescription, {}); + }); + + }); + + describe('Cycle de vérification des configurations', function() { + + it('checkServerConfiguration()', function() { + assert.equal(serverManager.checkServerConfiguration(configuration), true); + }); + + it('saveServerConfiguration()', function() { + serverManager.saveServerConfiguration(configuration); + let refObject = {}; + refObject[configuration.id] = configuration; + assert.deepEqual(serverManager._checkedServerDescription, refObject); + }); + + it('flushCheckedServer()', function() { + serverManager.flushCheckedServer(); + assert.deepEqual(serverManager._checkedServerDescription, {}); + }); + + }); + + describe('Chargement des configurations', function() { + + it('loadServerConfiguration()', function() { + assert.equal(serverManager.loadServerConfiguration(app, configuration), true); + }); + + }); + + describe('Cycle de vie des serveurs gérés', function() { + + it('startAllServers()', function() { + assert.equal(serverManager.startAllServers(), true); + }); + + it('stopAllServer()', function() { + assert.equal(serverManager.stopAllServer(), true); + }); + + }); + +}); diff --git a/test/integration/mocha/service/integrationService.js b/test/integration/mocha/service/integrationService.js index bd4a7e3..885e740 100644 --- a/test/integration/mocha/service/integrationService.js +++ b/test/integration/mocha/service/integrationService.js @@ -4,11 +4,10 @@ const RouteRequest = require('../../../../src/js/requests/routeRequest'); const ApisManager = require('../../../../src/js/apis/apisManager'); const ResourceManager = require('../../../../src/js/resources/resourceManager'); const SourceManager = require('../../../../src/js/sources/sourceManager'); -const TopologyManager = require('../../../../src/js/topology/topologyManager'); const ServerManager = require('../../../../src/js/server/serverManager'); const Resource = require('../../../../src/js/resources/resource'); const Source = require('../../../../src/js/sources/source'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); const path = require('path'); const fs = require('fs'); @@ -70,7 +69,6 @@ describe('Test de la classe Service', function() { sourceManager.getSourceTopology = sinon.stub().returns("toto"); service._sourceManager = sourceManager; - const topologyManager = sinon.mock(TopologyManager); topologyManager.getTopologyById = sinon.stub().returns("toto"); service._topologyManager = topologyManager; diff --git a/test/integration/mocha/service/integrationServiceProcess.js b/test/integration/mocha/service/integrationServiceProcess.js new file mode 100644 index 0000000..ff0b749 --- /dev/null +++ b/test/integration/mocha/service/integrationServiceProcess.js @@ -0,0 +1,44 @@ +const assert = require('assert'); +const ServiceProcess = require('../../../../src/js/service/serviceProcess'); +const logManager = require('../logManager'); +const path = require('path'); + +describe('Test de la classe ServiceProcess', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let configuration = path.resolve(__dirname, "../config/service.json"); + let serviceProcess = new ServiceProcess("test", configuration); + + describe('Test du constructeur et des attributs', function() { + + it('Get id', function() { + assert.equal(serviceProcess.id, "test"); + }); + + it('Get type', function() { + assert.equal(serviceProcess.type, "newProcess"); + }); + + it('Get _configurationLocation', function() { + assert.equal(serviceProcess._configurationLocation, configuration); + }); + + it('Get _serviceAdministered', function() { + assert.deepEqual(serviceProcess._serviceAdministered, {}); + }); + + }); + + describe('Test du chargement d\'un service', function() { + + xit('loadService()', function() { + assert.equal(serviceProcess.loadService(), true); + }); + + }); + +}); diff --git a/test/integration/mocha/sources/integrationPgrSource.js b/test/integration/mocha/sources/integrationPgrSource.js index b72f9e7..cbe498f 100644 --- a/test/integration/mocha/sources/integrationPgrSource.js +++ b/test/integration/mocha/sources/integrationPgrSource.js @@ -19,9 +19,13 @@ describe('Test de la classe pgrSource', function() { "storage": { "dbConfig": "/home/docker/app/test/unit/mocha/config/dbs/db_config_test.json", "costColumn": "cost_s_car", - "rcostColumn": "reverse_cost_s_car" + "rcostColumn": "reverse_cost_s_car", + "base": { + "schema" : "public", + "attributes" : {length: 0} + } }, - "cost": { + "costs": [{ "profile": "car", "optimization": "fastest", "compute": { @@ -30,6 +34,7 @@ describe('Test de la classe pgrSource', function() { } } } + ] }; let topology = { @@ -51,9 +56,13 @@ describe('Test de la classe pgrSource', function() { "type": "pgr", "storage": { "costColumn": "cost_m_car", - "rcostColumn": "reverse_cost_m_car" + "rcostColumn": "reverse_cost_m_car", + "base": { + "schema" : "public", + "attributes" : {length: 0} + } }, - "cost": { + "costs": [{ "profile": "car", "optimization": "shortest", "compute": { @@ -62,6 +71,7 @@ describe('Test de la classe pgrSource', function() { } } } + ] }; let source = new pgrSource(sourceDescription, topology); @@ -73,7 +83,7 @@ describe('Test de la classe pgrSource', function() { describe('Test du constructeur et des getters', function() { - it('Get Source id', function() { + xit('Get Source id', function() { assert.equal(source.id, "test-car-fastest"); }); diff --git a/test/integration/mocha/sources/integrationSourceManager.js b/test/integration/mocha/sources/integrationSourceManager.js index a3dde9c..29d1b1e 100644 --- a/test/integration/mocha/sources/integrationSourceManager.js +++ b/test/integration/mocha/sources/integrationSourceManager.js @@ -1,7 +1,7 @@ const assert = require('assert'); const SourceManager = require('../../../../src/js/sources/sourceManager'); const OperationManager = require('../../../../src/js/operations/operationManager'); -const logManager = require('../../../unit/mocha/logManager'); +const logManager = require('../logManager'); const Source = require('../../../../src/js/sources/source'); const sinon = require('sinon'); diff --git a/test/integration/mocha/sources/integrationValhallaSource.js b/test/integration/mocha/sources/integrationValhallaSource.js index fffb9d4..37ab5b9 100644 --- a/test/integration/mocha/sources/integrationValhallaSource.js +++ b/test/integration/mocha/sources/integrationValhallaSource.js @@ -39,7 +39,7 @@ describe('Test de la classe valhallaSource', function() { "dir": "/home/docker/data/corse-latest-valhalla-tiles/", "config": "/home/docker/data/valhalla.json" }, - "cost": { + "costs": [{ "profile": "car", "optimization": "fastest", "compute": { @@ -54,6 +54,7 @@ describe('Test de la classe valhallaSource', function() { } } } + ] }; let topology = { diff --git a/test/integration/mocha/topology/integrationTopologyManager.js b/test/integration/mocha/topology/integrationTopologyManager.js deleted file mode 100644 index 712b79a..0000000 --- a/test/integration/mocha/topology/integrationTopologyManager.js +++ /dev/null @@ -1,87 +0,0 @@ -const assert = require('assert'); -const TopologyManager = require('../../../../src/js/topology/topologyManager'); -const BaseManager = require('../../../../src/js/base/baseManager'); -const ProjectionManager = require('../../../../src/js/geography/projectionManager'); -const OsmTopology = require('../../../../src/js/topology/osmTopology'); -const logManager = require('../logManager'); -const fs = require('fs'); - -describe('Test de la classe TopologyManager', function() { - - before(function() { - // runs before all tests in this block - logManager.manageLogs(); - }); - - let baseManager = new BaseManager(); - let projectionManager = new ProjectionManager(); - projectionManager.loadProjectionDirectory("/home/docker/app/test/integration/mocha/config/projections"); - - let topologyManager = new TopologyManager(baseManager, projectionManager); - - describe('Test du checkTopology()', function() { - - it('checkTopology() avec une topologie OSM correcte', function() { - let correctOSMTopology = JSON.parse(fs.readFileSync("/home/docker/app/test/integration/resources/topology/correctOSMTopology.json")); - assert.equal(topologyManager.checkTopology(correctOSMTopology), true); - }); - - it('checkTopology() avec une topologie OSM incorrecte', function() { - let incorrectOSMTopology = JSON.parse(fs.readFileSync("/home/docker/app/test/integration/resources/topology/incorrectOSMTopology.json")); - assert.equal(topologyManager.checkTopology(incorrectOSMTopology), false); - }); - - it('checkTopology() avec une topologie DB correcte', function() { - let correctDBTopology = JSON.parse(fs.readFileSync("/home/docker/app/test/integration/resources/topology/correctDBTopology.json")); - assert.equal(topologyManager.checkTopology(correctDBTopology), true); - }); - - it('checkTopology() avec une topologie DB incorrecte', function() { - let incorrectDBTopology = JSON.parse(fs.readFileSync("/home/docker/app/test/integration/resources/topology/incorrectDBTopology.json")); - assert.equal(topologyManager.checkTopology(incorrectDBTopology), false); - }); - - }); - - describe('Test du checkDuplicationTopology()', function() { - - let correctOSMTopology = JSON.parse(fs.readFileSync("/home/docker/app/test/integration/resources/topology/correctOSMTopology.json")); - - it('checkDuplicationTopology() avec une topologie deja verifiee et identique', function() { - assert.equal(topologyManager.checkDuplicationTopology(correctOSMTopology), true); - }); - - it('checkDuplicationTopology() avec une topologie deja verifiee mais non identique', function() { - correctOSMTopology.description = "test"; - assert.equal(topologyManager.checkDuplicationTopology(correctOSMTopology), false); - }); - - }); - - describe('Test du createTopology()', function() { - - let correctOSMTopology = JSON.parse(fs.readFileSync("/home/docker/app/test/integration/resources/topology/correctOSMTopology.json")); - let referenceTopology = new OsmTopology(correctOSMTopology.id, correctOSMTopology.description, - correctOSMTopology.projection, correctOSMTopology.bbox, correctOSMTopology.storage.file); - - it('createTopology() avec une topologie deja verifiee', function() { - assert.deepEqual(topologyManager.createTopology(correctOSMTopology), referenceTopology); - }); - - }); - - describe('Test du loadAllTopologies()', function() { - - it('loadAllTopologies()', function() { - assert.deepEqual(topologyManager.loadAllTopologies(), true); - }); - - it('loadAllTopologies() pour un manager vide', function() { - let emptyTopologyManager = new TopologyManager(baseManager, projectionManager); - assert.deepEqual(emptyTopologyManager.loadAllTopologies(), false); - }); - - - }); - -}) \ No newline at end of file diff --git a/test/integration/readme.md b/test/integration/readme.md index 3bc24f3..011fd25 100644 --- a/test/integration/readme.md +++ b/test/integration/readme.md @@ -18,19 +18,22 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - point (geometry, proj4) - polygon (geoemtry, turf, proj4, polyline) - operation (parameter) - - resourceParameter (parameter)* - - routeRequest (request) - - isochroneRequest (request) - - serverManager (server, ExpressJS, log4js) + - resourceParameter (parameter) + - serverManager (server, ExpressJS, log4js, fs, assert) - Deuxième niveau: + - routeRequest (request, point) + - isochroneRequest (request, point) + - nearestRequest (request, point) - resourceOperation (resourceParameter) - boolParameter (resourceParameter) - enumParameter (resourceParameter) - floatParameter (resourceParameter) - pointParameter (resourceParameter, point, log4js) - constraintParameter (resourceParameter, constraint, looseConstraint) - - isochroneResponse (response, point, geometry) + - intParameter (resourceParameter) + - isochroneResponse (response, point, geometry)* + - nearestResponse (response, point, geometry) - step (line, duration, distance) - source (baseManager, projectionManager) @@ -49,7 +52,7 @@ Cinquième niveau: - routeResponse (response, point, route) Sixième niveau: - - osrmSource (source, osrm, routeResponse, route, portion, line, point, step, distance, duration, errorManager, log4js) + - osrmSource (source, osrm, routeResponse, nearestResponse, route, portion, line, point, step, distance, duration, errorManager, log4js) - pgrSource (source, routeResponse, isochroneResponse, route, portion, line, point, polygon, step, distance, duration, errorManager, gisManager, copyManager, simplify, turf, looseConstraint, log4js) Septième niveau: @@ -61,6 +64,13 @@ Huitième niveau: Neuvième niveau: - service (apisManager, resourceManager, sourceManager, operationManager, baseManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) +Dixième niveau: + - serviceManager (service, serviceProcess, log4js) + - serviceProcess (serviceAdministered, service, log4js, fork) + +Onzième niveau: + - administrator (express, log4js, helmet, path, fs, assert, serverManager, serviceManager, apisManager) + Autres: - road2.js - controller.js de l'api simple 1.0.0 diff --git a/test/integration/resources/topology/correctDBTopology.json b/test/integration/resources/topology/correctDBTopology.json deleted file mode 100644 index b3fdead..0000000 --- a/test/integration/resources/topology/correctDBTopology.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id": "base-bduni", - "type": "db", - "description": "Donn\u00e9es issues de la BDUNI de l'IGN.", - "storage": { - "base": { - "dbConfig": "/home/docker/data/output_base.json", - "schema": "public", - "attributes": [ - { - "key": "name", - "column": "way_names", - "default": "false" - }, - { - "key": "id", - "column": "id", - "default": "false" - } - ] - } - }, - "projection": "EPSG:4326", - "bbox": "1.7,48.4,3.3,49.1" -} \ No newline at end of file diff --git a/test/integration/resources/topology/correctOSMTopology.json b/test/integration/resources/topology/correctOSMTopology.json deleted file mode 100644 index 133bdfa..0000000 --- a/test/integration/resources/topology/correctOSMTopology.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "corse-osm", - "type": "osm", - "description": "Données OSM sur la Corse.", - "storage": { - "file": "/home/docker/internal/corse-latest.osm.pbf" - }, - "projection": "EPSG:4326", - "bbox": "-180,-90,180,90" -} \ No newline at end of file diff --git a/test/integration/resources/topology/incorrectDBTopology.json b/test/integration/resources/topology/incorrectDBTopology.json deleted file mode 100644 index a62074b..0000000 --- a/test/integration/resources/topology/incorrectDBTopology.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "db", - "description": "Donn\u00e9es issues de la BDUNI de l'IGN.", - "storage": { - "base": { - "dbConfig": "/home/docker/data/output_base.json", - "schema": "public", - "attributes": [ - { - "key": "name", - "column": "way_names", - "default": "false" - }, - { - "key": "id", - "column": "id", - "default": "false" - } - ] - } - }, - "projection": "EPSG:4326", - "bbox": "1.7,48.4,3.3,49.1" -} \ No newline at end of file diff --git a/test/integration/resources/topology/incorrectOSMTopology.json b/test/integration/resources/topology/incorrectOSMTopology.json deleted file mode 100644 index 8597726..0000000 --- a/test/integration/resources/topology/incorrectOSMTopology.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "osm", - "description": "Données OSM sur la Corse.", - "storage": { - "file": "/home/docker/internal/corse-latest.osm.pbf" - }, - "projection": "EPSG:4326", - "bbox": "-180,-90,180,90" -} \ No newline at end of file diff --git a/test/unit/mocha/config/resources/corse.resource b/test/unit/mocha/config/resources/corse.resource deleted file mode 100644 index f6e6da5..0000000 --- a/test/unit/mocha/config/resources/corse.resource +++ /dev/null @@ -1,130 +0,0 @@ -{ - "resource": { - "id": "corse-osm", - "type": "osrm", - "description": "Exemple d'une ressource sur la Corse avec les données OSM.", - "topology": { - "id": "corse-osm", - "type": "osm", - "description": "Données OSM sur la Corse.", - "storage": { - "file": "/home/docker/internal/corse-latest.osm.pbf" - }, - "projection": "EPSG:4326", - "bbox": "-180,-90,180,90" - }, - "sources": [ - { - "id": "corse-car-fastest", - "type": "osrm", - "storage": { - "file": "/home/docker/internal/corse-latest.osrm" - }, - "cost": { - "profile": "car", - "optimization": "fastest", - "compute": { - "storage": { - "file": "/usr/local/share/osrm/profiles/car.lua" - } - } - } - } - ], - "availableOperations":[ - { - "id": "route", - "parameters": [ - { - "id": "resource", - "values": [ - "corse-osm" - ] - }, - { - "id": "start", - "values": { - "bbox": "-180,-90,180,90", - "projection": "EPSG:4326" - } - }, - { - "id": "end", - "values": { - "bbox": "-180,-90,180,90", - "projection": "EPSG:4326" - } - }, - { - "id": "profile", - "defaultValueContent": "car", - "values": [ - "car" - ] - }, - { - "id": "optimization", - "defaultValueContent": "fastest", - "values": [ - "fastest" - ] - }, - { - "id": "intermediates", - "values": { - "bbox": "-180,-90,180,90", - "projection": "EPSG:4326" - } - }, - { - "id": "getSteps", - "defaultValueContent": "true" - }, - { - "id": "waysAttributes", - "values": [ - "name" - ] - }, - { - "id": "geometryFormat", - "defaultValueContent": "geojson", - "values": [ - "geojson", - "polyline" - ] - }, - { - "id": "bbox", - "defaultValueContent": "true" - }, - { - "id": "projection", - "defaultValueContent": "EPSG:4326", - "values": [ - "EPSG:4326", - "EPSG:2154" - ] - }, - { - "id": "timeUnit", - "defaultValueContent": "minute", - "values": [ - "hour", - "minute", - "second" - ] - }, - { - "id": "distanceUnit", - "defaultValueContent": "meter", - "values": [ - "meter", - "kilometer" - ] - } - ] - } - ] - } -} diff --git a/test/unit/mocha/requests/testsRequest.js b/test/unit/mocha/requests/testsRequest.js index c46f0bb..60a7f48 100644 --- a/test/unit/mocha/requests/testsRequest.js +++ b/test/unit/mocha/requests/testsRequest.js @@ -39,9 +39,10 @@ describe('Test de la classe Request', function() { assert.equal(request.resource, "corse-osm-2"); }); - it('Set Type', function() { + // Le type ne devrait pas changer car il dépend de la classe fille appelée + it('Set Type ne change rien', function() { request.type = "otherRequest"; - assert.equal(request.type, "otherRequest"); + assert.equal(request.type, "routeRequest"); }); }); diff --git a/test/unit/mocha/service/testsServiceAdministered.js b/test/unit/mocha/service/testsServiceAdministered.js new file mode 100644 index 0000000..2f79fb5 --- /dev/null +++ b/test/unit/mocha/service/testsServiceAdministered.js @@ -0,0 +1,26 @@ +const assert = require('assert'); +const ServiceAdministered = require('../../../../src/js/service/serviceAdministered'); +const logManager = require('../logManager'); + +describe('Test de la classe ServiceAdministered', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + describe('Test du constructeur et des getters', function() { + + let serviceAdm = new ServiceAdministered("test", "newProcess"); + + it('Get Id', function() { + assert.equal(serviceAdm.id, "test"); + }); + + it('Get Type', function() { + assert.equal(serviceAdm.type, "newProcess"); + }); + + }); + +}); diff --git a/test/unit/readme.md b/test/unit/readme.md index 9cd4908..f8dc270 100644 --- a/test/unit/readme.md +++ b/test/unit/readme.md @@ -9,7 +9,7 @@ docker-compose exec road2 npm run utest Mais cela devrait fonctionner uniquement avec `mocha`. Lancer la commande suivante depuis la racine du projet: ``` mocha --recursive './test/unit/mocha/**/*.js' -```  +``` Les tests unitaires concernent les classes qui ne dépendent pas d'une autre classe du projet pour fonctionner. Les autres classes sont testées dans les tests d'intégration [ici](./integration/readme.md). @@ -33,4 +33,5 @@ On trouvera donc les classes ou les fichiers suivants: - simplify.js //TODO - storageManager (log4js) - validationManager -- wkt \ No newline at end of file +- wkt +- serviceAdministered From 8758f7744d6cfb68ba8bf8cb6447de2ab9f250e1 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 11 Jan 2023 15:19:37 +0100 Subject: [PATCH 49/93] feat(version): update version to 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e5f0bf..d612e10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "2.0.0-DEVELOP", + "version": "2.0.0", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", From 3d93fad72bb8625ed1dcf92e9867fe969a977803 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Tue, 17 Jan 2023 09:55:39 +0100 Subject: [PATCH 50/93] feat(doc) move documentation and init sphynx build --- changelog.md | 30 +++--- docker/readme.md | 13 --- documentation/conf.py | 100 ++++++++++++++++++ documentation/developers/concepts.md | 38 +++---- documentation/developers/documentation.md | 26 +++++ documentation/developers/functionnalities.md | 70 ++++++------ documentation/developers/history.md | 2 + .../docker}/demonstration/readme.md | 0 .../docker}/dev/readme.md | 0 .../docker/distributions}/readme.md | 12 +-- documentation/docker/readme.md | 13 +++ .../docker}/test/readme.md | 8 +- .../docker}/web/readme.md | 6 +- documentation/index.md | 69 ++++++++++++ .../test}/functional/readme.md | 0 .../test}/integration/readme.md | 18 ++-- {test => documentation/test}/load/readme.md | 0 {test => documentation/test}/readme.md | 0 {test => documentation/test}/unit/readme.md | 2 +- requirements/documentation.txt | 8 ++ 20 files changed, 309 insertions(+), 106 deletions(-) delete mode 100644 docker/readme.md create mode 100644 documentation/conf.py create mode 100644 documentation/developers/documentation.md create mode 100644 documentation/developers/history.md rename {docker => documentation/docker}/demonstration/readme.md (100%) rename {docker => documentation/docker}/dev/readme.md (100%) rename {docker/distributions/debian => documentation/docker/distributions}/readme.md (91%) create mode 100644 documentation/docker/readme.md rename {docker => documentation/docker}/test/readme.md (93%) rename {docker => documentation/docker}/web/readme.md (97%) create mode 100644 documentation/index.md rename {test => documentation/test}/functional/readme.md (100%) rename {test => documentation/test}/integration/readme.md (96%) rename {test => documentation/test}/load/readme.md (100%) rename {test => documentation/test}/readme.md (100%) rename {test => documentation/test}/unit/readme.md (92%) create mode 100644 requirements/documentation.txt diff --git a/changelog.md b/changelog.md index e3ee51e..2b9c867 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,6 @@ -# 2.0.0 +# CHANGELOG + +## 2.0.0 ADDED: - La classe Administrator permet de gérer le service via une API. Notamment la création, la suppression et la modification d'un service seront possible. @@ -30,43 +32,43 @@ UPDATED: - Passage à `pgrouting-procedures` 2.0.0 - Passage à `route-graph-generator` 1.2.3 -# 1.1.2 +## 1.1.2 FIXED: - Géométries reprojetées pour les requêtes sur PGRouting -# 1.1.1 +## 1.1.1 FIXED: - Géométrie étrange quand on est dans une raquette -# 1.1.0 +## 1.1.0 ADDED: - Ajout de la fonctionnalité nearest via OSRM -# 1.O.14 +## 1.O.14 FIXED: - Mauvaise ligne pour un log -# 1.O.13 +## 1.O.13 ADDED: - Pas d'erreur si certificat auto-signé ou périmé -# 1.O.12 +## 1.O.12 FIXED: - got dans bundledDependencies - http-proxy-agent dans bundledDependencies -# 1.0.11 +## 1.0.11 FIXED: - wkt dans bundledDependencies -# 1.0.10 +## 1.0.10 ADDED: - Ressource hybride smartrouting / pgr pour l'isochrone -# 1.0.6 +## 1.0.6 ADDED: - chaque source peut donner l'état de sa connexion @@ -78,7 +80,7 @@ CHANGED: FIXED: - géométrie des itinéraires invalide lorsqu'après être passé par un point intermédiaire, l'itinéraire doit reprendre le même tronçon en sens inverse -# 1.0.5 +## 1.0.5 FIXED: - gestion d'une erreur PGR si aucun isochrone n'est trouvé. @@ -96,7 +98,7 @@ ADDED: - gestion des proxy http pour les tests fonctionnels - lecture du hostname dans la request et adaptation du getcapabilities -# 1.0.4 +## 1.0.4 CHANGED: - suppresion des dossiers temporaires dans les tests fonctionnels @@ -105,7 +107,7 @@ FIXED: - Plus d'erreur dans l'isochrone quand la costValue est trop basse pour avoir un polygone - min et max de costValue n'etaient pas dans l'objet values -# 1.0.3 +## 1.0.3 CHANGED: - Le port HTTPS peut être n'import quel port @@ -114,7 +116,7 @@ CHANGED: FIXED: - La vérification des sources est plus fine quant au type des opérations possibles (ajout de l'isochrone). -# 1.0.2 +## 1.0.2 ADDED: - Modification du server.json: emplacement du fichier de configuration des CORS diff --git a/docker/readme.md b/docker/readme.md deleted file mode 100644 index e221985..0000000 --- a/docker/readme.md +++ /dev/null @@ -1,13 +0,0 @@ -# Utiliser Road2 avec Docker - -Ce dossier regroupe les différents fichiers permettant d'utiliser Road2 avec docker. - -Il y a un sous-dossier pour les grands usages identifiés : -- [dev](./dev/) : développer Road2 -- [demonstration](./demonstration/) : obtenir une démonstration locale des services proposés par Road2. Ce Dockerfile est limité aux tests des moteurs OSRM et PGRouting car il n'existe pas de bindings Valhalla pour le moment. -- [test](./test/) : Tester Road2 - -D'autres sous-dossiers sont ordonnés ainsi pour des raisons pratiques : -- [web](./web/) : Ce dossier regroupe des fichiers utiles pour avoir un petit site web qui contient plusieurs documentations et des pages de tests graphiques pour Road2. -- [config](./config/) : Ce dossier regroupe plusieurs fichiers de configurations qui se trouvent être communs aux autres sous-dossiers. -- [distributions](./distributions/) : Ce dossier peut regrouper différents `Dockerfile` qui sont des exemples d'installation sous différentes distributions. Actuellement, il ne reste plus qu'un exemple pour Debian. C'est le Dockerfile préconisé pour développer sur Road2 car il contient tous les binaires utiles aux différents moteurs. \ No newline at end of file diff --git a/documentation/conf.py b/documentation/conf.py new file mode 100644 index 0000000..18bf869 --- /dev/null +++ b/documentation/conf.py @@ -0,0 +1,100 @@ +#!python3 + +""" + Configuration for project documentation using Sphinx. +""" + +# standard +import sys +from datetime import datetime +from os import environ, path + +sys.path.insert(0, path.abspath("..")) # move into project package + +# -- Build environment ----------------------------------------------------- +on_rtd = environ.get("READTHEDOCS", None) == "True" + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + # Sphinx included + "sphinx.ext.autosectionlabel", + "sphinx.ext.extlinks", + "sphinx.ext.githubpages", + "sphinx.ext.imgmath", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + # 3rd party + "myst_parser", + "sphinx_copybutton", + "sphinx.ext.napoleon", +] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +source_suffix = {".md": "markdown", ".rst": "restructuredtext"} +autosectionlabel_prefix_document = True +# The master toctree document. +master_doc = "index" + + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = [ + "_build", + ".venv", + "Thumbs.db", + ".DS_Store", + "_output", + "demo", +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + + +# -- Options for HTML output ------------------------------------------------- + +# -- Theme + +#html_favicon = str(__about__.__icon_path__) +#html_logo = str(__about__.__icon_path__) +html_static_path = ["_static"] +html_theme = "sphinx_book_theme" +html_theme_options = { + "home_page_in_toc": True, + "path_to_docs": "docs", + "repository_branch": "develop", + "show_toc_level": 3, + +} + +# -- EXTENSIONS -------------------------------------------------------- +# MyST Parser +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "html_image", + "linkify", + "replacements", + "smartquotes", + "substitution", +] + +myst_substitutions = { + #"author": author, + "date_update": datetime.now().strftime("%d %B %Y"), + #"description": description, + #"repo_url": __about__.__uri__, + #"title": project, + #"version": version, + #"license": license, +} + +myst_url_schemes = ["http", "https", "mailto"] diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index 5d04b8b..ac141ad 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -4,11 +4,11 @@ Ce chapitre décrit plusieurs concepts logiciels utilisés dans Road2. La plupar ## Partie 1 : Modularité de l'application -### 1.1 Indépendance entre les APIs et les moteurs +### Indépendance entre les APIs et les moteurs C'est le concept de base pour comprendre le code de Road2. -#### 1.1.1 Notions d'API +#### Notions d'API **Une API, pour Road2, est un ensemble de routes que le serveur reconnaît et regroupe au sein d'une même appellation**. Pour chaque appellation, il y aura potentiellement plusieurs versions. Et au sein de chaque version, il y aura potentiellement plusieurs routes. @@ -16,7 +16,7 @@ Par exemple, si on considère une API qui s'appelle `rest` qui ne possède qu'un Chaque API est définie dans un dossier distinct des autres. Cela les rend indépendantes les unes des autres. Et pour une même appellation, on a une indépendance entre deux versions différentes. On trouvera des exemples d'implémentation dans le dossier des [apis du code](../../src/js/apis/). -#### 1.1.2 Notion de moteur +#### Notion de moteur **Un moteur, pour Road2, est une brique logicielle qui peut effectuer divers calculs**. Cette brique peut être une librairie, un autre service web, une base de données, etc... @@ -24,7 +24,7 @@ Par exemple, OSRM est un moteur qui est écrit en C++ et qui propose une envelop Au passage, il semble utile de préciser ici que chaque moteur est indépendant des autres par son implémentation dans le code du projet (cf. la notion de source plus bas). -#### 1.1.3 Notion de service +#### Notion de service Road2 a été codé pour faciliter la gestion des APIs et des moteurs. Pour atteindre cet objectif, la partie API et la partie moteur sont séparées et aucune ne voit ce que fait l'autre. @@ -32,11 +32,11 @@ Une API va donc devoir créer un objet requête générique qui sera envoyé à Cela permet d'ajouter ou supprimer une API sans qu'une telle modification impacte les moteurs. Et inversement. -### 1.2 Lien entre les ressources et les sources +### Lien entre les ressources et les sources C'est le second concept le plus important après l'indépendance des APIs et des moteurs. Il est nécessaire de le comprendre pour développer sur le projet. -#### 1.2.1 Notion de graphe +#### Notion de graphe Il semble utile de passer la notion de *graphe*, selon Road2, pour expliquer ce qui suit. Quand on fait du calcul d'itinéraire, on utilise un moteur qui lit un *graphe* pour générer l'itinéraire. Or, **un *graphe* est une topologie, c'est-à-dire un ensemble de noeuds et d'arcs qui forment un tout navigable, sur laquelle il y a au moins un coût**. @@ -44,7 +44,7 @@ En effet, à chaque arc est associé au minimum un coût. Ce coût peut être la Certains graphes peuvent avoir plusieurs coûts par topologie (ex. PGRouting, Valhalla) et d'autres non (ex. OSRM). Mais lors d'un calcul d'itinéraire, un seul coût est utilisé. -#### 1.2.2 Notion de source +#### Notion de source Comme précisé juste au-dessus, pour avoir un itinéraire, il est nécessaire de faire appel à un moteur qui utilise un graphe. La *source*, dans le langage conceptuel de Road2, est l'origine du calcul. **La source contient l'appel à un moteur sur un graphe précis pour obtenir le résultat d'un calcul**. C'est le lien entre l'application et le calcul réel, comme celui d'un itinéraire par exemple. @@ -62,7 +62,7 @@ Lorsque l’on fait du calcul d’itinéraire, il faut à minima une topologie e Il se trouve qu'un graphe OSRM ne contient qu’un seul coût par dossier. Il permet donc de calculer des itinéraires uniquement sur un seul mode de déplacement et une seule optimisation. Par contre, PgRouting propose autant de colonnes de coût que l'on souhaite sur une même topologie. On retrouve le même regroupement de couples sur une topologie dans Valhalla. -#### 1.2.3 Notion de ressource +#### Notion de ressource Cependant, pour l'utilisateur et pour l'administrateur du service, nous avons créé la notion de *ressource*. **Une ressource sera définie comme un ensemble de sources**. C'est elle qui fait le lien entre une requête et la bonne source permettant d'y répondre. @@ -78,7 +78,7 @@ Il est à noter que tout cela peut d'ailleurs avoir un impact sur les contrainte Enfin, précisons que Road2 est codé pour qu'il soit facile d'ajouter de nouveaux types de ressources et de sources indépendamment. Il est donc possible de créer différents types de source et de les associer au sein de divers types de ressources. -### 1.3 Les opérations +### Les opérations Une opération est un calcul que l'on veut réaliser. Un calcul d'itinéraire, un calcul d'isochrone, un distancier sont des exemples d'opérations attendues. Or, un moteur donné ne peut pas forcément réaliser toutes ces opérations. Il se peut que l'un puisse faire des itinéraires et des distancier mais pas des isochrones. Il est donc nécessaire de savoir ce qu'un moteur peut faire. @@ -86,7 +86,7 @@ De plus, une opération donnée peut être plus ou moins gourmandes en ressource Road2 intègre donc la notion d'opération pour gérer ces différentes problématiques. -#### 1.3.1 Les paramètres +#### Les paramètres Chaque opération possède des paramètres pour pouvoir effectuer un calcul. La plupart des paramètres peuvent se regrouper dans des catégories. Par exemple, un paramètre pourra être un mot clé issue d'une liste ou un point représentant des coordonnées. @@ -94,19 +94,19 @@ Au sein de ces catégories, la vérification de la validité d'un paramètre sui Afin de mutualiser le code, des classes de paramètres ont été créées. Et elles peuvent être utilisées n'importe où dans le code. On trouvera un exemple d'utilisation de ces classes dans l'api `simple/1.0.0`. -### 1.4 Interface : requests et responses +### Interface : requests et responses Maintenant, il est possible de parler avec plus de détails de l'interface qu'il y a entre une API donnée et un moteur. Comme précisé plus haut, le moteur n'a pas connaissance des APIs et les APIs ne connaissent pas les moteurs. Ainsi, pour communiquer, il y a une interface qui se résume à deux classes d'objets Javascript : `Request` et `Response`. -#### 1.4.1 L'objet Request +#### L'objet Request La classe `Request` est considérée comme une classe mère. À partir d'elle, on peut créer autant de classe fille que l'on veut. Chaque instance d'une classe fille `request` est une requête générique qui sera transmise à un moteur. Ce dernier ne saura donc pas quelle API l'a interrogé mais il aura toutes les informations utiles pour effectuer le calcul demandé. -#### 1.4.2 L'objet Response +#### L'objet Response Quand un moteur a fini son calcul, il crée un objet qui lui est propre. Mais pour être compris par une API, il doit créer un objet `response`, classe fille de `Response`, qui représente une réponse générique que chaque API peut comprendre. L'API ne sait donc pas quel moteur a fait le calcul mais elle a toutes les informations utiles pour répondre à l'utilisateur selon le formalisme attendu. -### 1.5 Les contraintes +### Les contraintes Road2 a développé la notion de contrainte pour permettre de calculs d'itinéraire plus complexes. Une contrainte est une condition que l'on donne à Road2 et qu'il traduit aux différents moteurs qui supportent ces conditions. @@ -118,11 +118,11 @@ Ces conditions ont été généralisées. En plus de pouvoir interdire, on peut Cette partie décrit l'application de ces concepts dans le code au cours d'une exécution classique. -### 2.1 Au lancement de l'application +### Au lancement de l'application Le projet Road2 propose deux serveurs web, un service et un administrateur. Il donc possède deux points d'entrée selon l'usage que l'on souhaite en faire. On peut lancer uniquement le service et cela fonctionnera très bien. Et on peut aussi lancer un administrateur uniquement. Celui-ci lancera un service quand on le lui demandera. Enfin, on peut lancer les deux d'un coup. -#### 2.1.1 Lancement de l'administrateur +#### Lancement de l'administrateur Le premier point d'entrée possible est le fichier `src/js/road2.js`. Ce fichier va générer une instance de la classe `Administrator`. @@ -134,7 +134,7 @@ Un administrateur a été créé pour réaliser des tâches qui auraient gêné L'administrateur a donc été créé pour être indépendant du service. Si l'administrateur a des tâches fastidieuses, cela n'impacte pas le service. Si l'un tombe, l'autre non. -#### 2.1.2 Lancement d'un service +#### Lancement d'un service Le point d'entrée historique est le fichier `src/js/service/main.js`. Ce fichier va générer une instance de la classe `Service`. @@ -148,7 +148,7 @@ Après cela, on charge les ressources et les sources du service indiquées dans Enfin, on finit par charger les APIs exposées par le service. C'est là qu'ExpressJS crée le ou les serveurs Node et charge les routes disponibles. -#### 2.1.3 Zoom sur la vérification de la configuration +#### Zoom sur la vérification de la configuration Que ce soit un administrateur ou un service, la configuration sera vérifiée. @@ -166,7 +166,7 @@ Pour bien fonctionner, le manager aura donc deux listes. Une liste plutôt éph La deuxième liste sera une liste des configurations déjà chargées. Cette liste est persistante et indique l'état du manager. Elle sert à s'assurer que l'on charge une seule fois chaque configuration même si elle est demandée plusieurs fois. Aussi, quand on souhaitera modifier la configuration durant la vie de l'application, c'est cette liste qui sera considérée la première pour vérifier la cohérence. La première liste ne sera réutilisée que si c'est un ensemble censé être cohérent que l'on vérifie. -### 2.2 : A la réception d'une requête +### A la réception d'une requête Lorsqu'une requête arrive, elle est traitée par le router d'ExpressJS de l'API appelée. Il est possible de faire les traitements que l'on veut au sein de ce router. Ces traitements peuvent n'avoir aucun rapport avec le reste de l'application. C'est un router express au sens basique du framework. diff --git a/documentation/developers/documentation.md b/documentation/developers/documentation.md new file mode 100644 index 0000000..b3e9e5b --- /dev/null +++ b/documentation/developers/documentation.md @@ -0,0 +1,26 @@ +# Documentation + +Sphinx est utilisé pour la génération de la documentation depuis des pages écrites en Markdown (via le [parser MyST](https://myst-parser.readthedocs.io/en/latest/)). + +## Génération du site web de documentation + +Pour la génération: + +```bash +# install aditionnal dependencies +python -m pip install -U -r requirements/documentation.txt +# build it +sphinx-build -b html docs docs/_build +# optimized (quiet, multiprocessing, doctrees separated) +sphinx-build -b html -d docs/_build/cache -j auto -q docs docs/_build/html +``` + +Ouvrir `docs/_build/index.html` dans un navigateur web. + +## Ecrire la documentation avec un rendu en direct + +```bash +sphinx-autobuild -b html -d docs/_build/cache docs/ docs/_build +``` + +Ouvrir dans un navigateur web pour voir le rendu HTML mis à jour quand un fichier est sauvegardé. diff --git a/documentation/developers/functionnalities.md b/documentation/developers/functionnalities.md index 14dfe57..e4c8433 100644 --- a/documentation/developers/functionnalities.md +++ b/documentation/developers/functionnalities.md @@ -6,38 +6,38 @@ Road2 propose un ensemble de fonctionnalités regroupées dans plusieurs groupes ## Groupe de fonctionnalités 1 : Interface pour calculer des itinéraires -### 1.1 : Définir un point de départ, un point d'arrivée et des points intermédiaires +### Définir un point de départ, un point d'arrivée et des points intermédiaires Fonctionnalités classiques et inévitables, elles ne seront pas détaillées ici. Précision : il est possible de préciser autant de point intermédiaire que l'on souhaite. -### 1.2 : Définir des contraintes +### Définir des contraintes La notion de contrainte est définie dans les [concepts](./concepts.md). Pour faire simple, il s'agit de spécifier des conditions que l'itinéraire devra remplir. La plus connue est certainement l'interdiction de prendre des autoroutes. Mais on peut avoir des conditions beaucoup plus complexes et on peut en appliquer plusieurs au calcul d'un itinéraire. -### 1.3 : Préciser la ressource employée +### Préciser la ressource employée Comme précisé dans les [concepts](./concepts.md), Road2 gère des ressources. Chaque requête doit préciser la ressources qu'elle interroge. -### 1.4 : Préciser le profile voulu +### Préciser le profile voulu Il est possible de préciser quel moyen de transport, l'itinéraire concerne. -### 1.5 : Préciser l'optimisation voulue +### Préciser l'optimisation voulue Il est possible de préciser l'optimisation que l'on veut appliquer lors du calcul. -### 1.6 : Ajouter des informations sur chaque tronçons +### Ajouter des informations sur chaque tronçons Selon les données présentes dans les graphes, il est possible de choisir les informations que l'on souhaite récupérer dans la réponse du calcul. -### 1.7 : Préciser le contenu de la réponse +### Préciser le contenu de la réponse Par l'intermédiaire de plusieurs paramètres, il est possible de préciser le contenu de la réponse : - La présence ou non des étapes du parcours. - Le format des géométries dans la réponse. Pour le moment, geojson, polyline et wkt sont disponibles. - La présence ou non d'une bbox dans la réponse. -### 1.8 : Choisir les unités de la requête et de la réponse +### Choisir les unités de la requête et de la réponse Grâce à des paramètres de la requête, il est possible d'influencer le format de la requête elle-même, et de la réponse : - Il est possible de définir la projection employée. @@ -49,40 +49,40 @@ Grâce à des paramètres de la requête, il est possible d'influencer le format ## Groupe de fonctionnalités 2 : Interface pour calculer des isochrones et des isodistances -### 2.1 : Définir un point de départ ou d'arrivée +### Définir un point de départ ou d'arrivée Fonctionnalité inévitable pour le calcul d'un isochrone. -### 2.2 : Préciser la ressource employée +### Préciser la ressource employée Comme précisé dans les [concepts](./concepts.md), Road2 gère des ressources. Chaque requête doit préciser la ressources qu'elle interroge. -### 2.3 : Préciser le type de coût employé +### Préciser le type de coût employé Divers coûts sont possibles pour le calcul, il s'agit donc de le préciser. -### 2.4 : Définir la valeur du coût employée +### Définir la valeur du coût employée Fonctionnalités inévitable pour un tel calcul. -### 2.5 : Préciser le profil voulu +### Préciser le profil voulu Il est possible de préciser quel moyen de transport, l'isochrone concerne. -### 2.6 : Indiquer la direction considérée pour le résultat du calcul +### Indiquer la direction considérée pour le résultat du calcul Un isochrone peut se définir dans deux directions : départ ou arrivée. Il s'agit donc de préciser laquelle. -### 2.7 : Définir des contraintes +### Définir des contraintes La notion de contrainte est définie dans les [concepts](./concepts.md). Elle est limitée aux interdictions, comme par exemple, l'interdiction de prendre des autoroutes. -### 2.8 : Préciser le contenu de la réponse +### Préciser le contenu de la réponse Par l'intermédiaire de plusieurs paramètres, un seul pour le moment, il est possible de préciser le contenu de la réponse : - Le format des géométrie dans la réponse. Pour le moment, geojson, polyline et wkt sont disponibles. -### 2.9 : Choisir les unités de la requête et de la réponse +### Choisir les unités de la requête et de la réponse Grâce à des paramètres de la requête, il est possible d'influencer le format de la requête elle-même, et de la réponse : - Il est possible de définir la projection employée. @@ -90,48 +90,44 @@ Grâce à des paramètres de la requête, il est possible d'influencer le format - Le format des distances est modifiable. - - ## Groupe de fonctionnalités 3 : Proposer un service web Road2 prend la forme d'un serveur web qui fournit un service de calcul d'itinéraire. Il possède donc plusieurs fonctionnalités liées à cela. -### 3.1 : Utiliser les protocoles HTTP et HTTPS +### Utiliser les protocoles HTTP et HTTPS Fonctionnalité explicite. -### 3.2 : Configurer les CORS +### Configurer les CORS Fonctionnalité explicite. -### 3.3 : Disposer de plusieurs APIs +### Disposer de plusieurs APIs Comme présenté dans les [concepts](./concepts.md), Road2 offre la possibilité de proposer aux clients différentes APIs simultanément. -### 3.4 : Disposer de plusieurs moteurs +### Disposer de plusieurs moteurs Comme présenté dans les [concepts](./concepts.md), Road2 offre la possibilité de proposer aux clients différents moteurs simultanément. - - ## Groupe de fonctionnalités 4 : Administrer le service Le service peut être administrer de deux manières : la configuration et une API dédiée. -### 4.1 : Configurer les ressources via la configuration +### Configurer les ressources via la configuration Via la configuration fichier du serveur, il est possible de créer des [ressources](./concepts.md) qui se baseront sur une ou plusieurs [sources](./concepts.md). -### 4.2 : Limiter certains usages via la configuration +### Limiter certains usages via la configuration Les APIs de Road2 proposent plusieurs options, comme la possibilité de calculer des itinéraires avec des points intermédiaires. Il peut être intéressant de limiter l'usage de ces options afin de ne pas surcharger le service. -### 4.3 : Obtenir la version du serveur via l'API +### Obtenir la version du serveur via l'API Fonctionnalité explicite. -### 4.4 : Obtenir l'état de santé du serveur via l'API +### Obtenir l'état de santé du serveur via l'API Fonctionnalité en cours d'implémentation. L'objectif est de récupérer l'état de chaque [source](./concepts.md) du serveur et d'en faire un compte-rendu. @@ -139,19 +135,19 @@ Fonctionnalité en cours d'implémentation. L'objectif est de récupérer l'éta ## Groupe de fonctionnalités 5 : Fonctionnalités spécifiques à OSRM -### 5.1 : Optimisation des graphes pour des réponses plus rapides +### Optimisation des graphes pour des réponses plus rapides Fonctionnalité explicite. -### 5.2 : Calcul des itinéraires +### Calcul des itinéraires Fonctionnalité explicite. -### 5.3 : Gestions des contraintes simples ou exclusions +### Gestions des contraintes simples ou exclusions Les exclusions sont les contraintes classiques comme l'interdiction d'emprunter certains type de voies (ex. autoroutes). Ce sont les seuls contraintes disponibles via OSRM. -### 5.4 : Déterminer le point du graphe le plus proche +### Déterminer le point du graphe le plus proche Pour un point donnée, OSRM peut renovyer les points les plus proches du graphe. @@ -159,22 +155,22 @@ Pour un point donnée, OSRM peut renovyer les points les plus proches du graphe. ## Groupe de fonctionnalités 6 : Fonctionnalités spécifiques à PGRouting -### 6.1 : Calcul d'itinéraire +### Calcul d'itinéraire Fonctionnalité explicite. -### 6.1 : Calcul d'isochrone +### Calcul d'isochrone Fonctionnalité explicite. -### 6.1 : Gestion de contraintes complexes +### Gestion de contraintes complexes PGRouting gère tout les types de contraintes, des plus simples au plus complexes. On peut donc interdire l'accès aux autoroutes ou préférer les routes qui ont une largeur supérieure à 5 mètres. ## Groupe de fonctionnalités 7 : Fonctionnalités spécifiques à l'API simple/1.0.0 -### 7.1 : Obtenir un GetCapabilities +### Obtenir un GetCapabilities Le GetCapabilities est une réponse JSON qui décrit les paramètres de chaque opérations et les valeurs disponibles pour une instance de Road2. diff --git a/documentation/developers/history.md b/documentation/developers/history.md new file mode 100644 index 0000000..2d8f6a0 --- /dev/null +++ b/documentation/developers/history.md @@ -0,0 +1,2 @@ +```{include} ../../changelog.md +``` diff --git a/docker/demonstration/readme.md b/documentation/docker/demonstration/readme.md similarity index 100% rename from docker/demonstration/readme.md rename to documentation/docker/demonstration/readme.md diff --git a/docker/dev/readme.md b/documentation/docker/dev/readme.md similarity index 100% rename from docker/dev/readme.md rename to documentation/docker/dev/readme.md diff --git a/docker/distributions/debian/readme.md b/documentation/docker/distributions/readme.md similarity index 91% rename from docker/distributions/debian/readme.md rename to documentation/docker/distributions/readme.md index 6cc5f54..6fb4f31 100644 --- a/docker/distributions/debian/readme.md +++ b/documentation/docker/distributions/readme.md @@ -1,21 +1,21 @@ # Dockerfile pour utiliser Road2 sous Debian -# Construction de l'image +## Construction de l'image Pour construire l'image, il suffit de lancer la commande suivante à la racine du projet Road2: ``` docker build -t road2-debian -f docker/debian/Dockerfile . ``` -# Lancer l'application +## Lancer l'application Pour lancer l'application, il suffit d'utiliser la commande suivante: ``` docker run --name road2-debian-server --rm -d -p 8080:8080 road2-debian ``` -## Mode DEBUG +### Mode DEBUG ``` docker run --name road2-debian-server --rm -it -p 8080:8080 road2-debian /bin/bash ``` @@ -30,21 +30,21 @@ docker run --name road2-debian-server --rm -d -p 8080:8080 -v $src:/home/docker/ docker run --name road2-debian-server --rm -it -p 8080:8080 -v $src:/home/docker/app/src road2-debian /bin/bash ``` -# Lancer les tests +## Lancer les tests Les tests unitaires ont été écrits avec Mocha. Pour les lancer, on utilisera la commande suivante: ``` docker run --name road2-debian-server --rm -v $src:/home/docker/app/src -v $test:/home/docker/app/test road2-debian npm run utest ``` -# Lancer eslint +## Lancer eslint Pour linter le code, il suffit de lancer la commande suivante: ``` docker run --name road2-debian-server --rm -v $src:/home/docker/app/src road2-debian npm run lint ``` -# Créer la documentation du code via jsdoc +## Créer la documentation du code via jsdoc Le code est documenté via des commentaires. Ces commentaires peuvent être plus ou moins structurés avec des tags. L'outil jsdoc permet de générer un site web à partir de ces commentaires et de ces tags. diff --git a/documentation/docker/readme.md b/documentation/docker/readme.md new file mode 100644 index 0000000..c7478cb --- /dev/null +++ b/documentation/docker/readme.md @@ -0,0 +1,13 @@ +# Utiliser Road2 avec Docker + +Ce dossier regroupe les différents fichiers permettant d'utiliser Road2 avec docker. + +Il y a un sous-dossier pour les grands usages identifiés : +- [dev](./dev/readme.md) : développer Road2 +- [demonstration](./demonstration/readme.md) : obtenir une démonstration locale des services proposés par Road2. Ce Dockerfile est limité aux tests des moteurs OSRM et PGRouting car il n'existe pas de bindings Valhalla pour le moment. +- [test](./test/readme.md) : Tester Road2 + +D'autres sous-dossiers sont ordonnés ainsi pour des raisons pratiques : +- [web](./web/readme.md) : Ce dossier regroupe des fichiers utiles pour avoir un petit site web qui contient plusieurs documentations et des pages de tests graphiques pour Road2. +- [config](./config/) : Ce dossier regroupe plusieurs fichiers de configurations qui se trouvent être communs aux autres sous-dossiers. +- [distributions](./distributions/readme.md) : Ce dossier peut regrouper différents `Dockerfile` qui sont des exemples d'installation sous différentes distributions. Actuellement, il ne reste plus qu'un exemple pour Debian. C'est le Dockerfile préconisé pour développer sur Road2 car il contient tous les binaires utiles aux différents moteurs. \ No newline at end of file diff --git a/docker/test/readme.md b/documentation/docker/test/readme.md similarity index 93% rename from docker/test/readme.md rename to documentation/docker/test/readme.md index 6b476f8..71f0c8a 100644 --- a/docker/test/readme.md +++ b/documentation/docker/test/readme.md @@ -1,15 +1,15 @@ # Docker-compose pour faire des tests -# Construction et utilisation avec docker-compose +## Construction et utilisation avec docker-compose -## Pré-requis +### Pré-requis Pour utiliser `docker-compose`, il suffit de : - installer `docker`. - se placer dans le dossier `/docker/test/` du projet Road2. - créer un fichier `.env` à côté du `docker-compose.yml` qui sera une copie adaptée du `compose.env.example` -## Construction des images +### Construction des images Il possible d'utiliser les Dockerfiles de chaque projet pour builder les images une par une. Mais cela peut se faire automatiquement via docker-compose. @@ -47,6 +47,6 @@ Cette commande lance la génération d'un fichier `ssv` dans un volume docker. C docker-compose up load-road2 ``` -## Utiliser des données et scénarii de la machine hôte +### Utiliser des données et scénarii de la machine hôte Si on souhaite utiliser des données et des scénarii stockés sur la machine hôte, il suffira de modifier le `.env` pour pointer vers un autre `user-files`. diff --git a/docker/web/readme.md b/documentation/docker/web/readme.md similarity index 97% rename from docker/web/readme.md rename to documentation/docker/web/readme.md index ec64dec..3eab043 100644 --- a/docker/web/readme.md +++ b/documentation/docker/web/readme.md @@ -5,14 +5,14 @@ Cette image permet de : - visualiser la documentation de l'API et du code. -# Construction de l'image +## Construction de l'image Pour construire l'image, il suffit de lancer la commande suivante à la racine du projet Road2: ``` docker build -t web-road2 -f docker/web/Dockerfile . ``` -# Lancer le serveur web +## Lancer le serveur web Pour lancer le serveur web qui rend la page accessible, il suffit d'utiliser la commande suivante: ``` @@ -24,7 +24,7 @@ docker run --name web-road2-page --rm -d -p 8080:80 web-road2 docker run --name web-road2-page --rm -d -p 8080:80 -v $src:/home/docker/web/www/road2 web-road2 ``` -# Tester Road2 +## Tester Road2 On pourra tester Road2 sur le lien suivant: http://localhost:8080/road2/ diff --git a/documentation/index.md b/documentation/index.md new file mode 100644 index 0000000..56e1c96 --- /dev/null +++ b/documentation/index.md @@ -0,0 +1,69 @@ +# Road2 + +## Présentation générale + +Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascript et conçu pour fonctionner avec NodeJS. Ce serveur propose le calcul d'itinéraires et d'isochrones via des moteurs existants comme [OSRM](https://github.com/Project-OSRM/osrm-backend) ou [PGRouting](https://pgrouting.org/). Road2 est donc une interface pour moteurs de calculs. Ces derniers ne sont pas fait dans le code de Road2 mais via des appels à ses moteurs. Cela peut se traduire par l'appel à une librairie, ou à une base de données, ou encore à un autre service web. + +Road2 a été conçu dans l'idée de pouvoir facilement ajouter des nouveaux moteurs et de nouvelles APIs, et cela, de manière totalement transparente les uns pour autres. Autrement dit, ajouter un moteur n'a pas d'impact sur les APIs déjà existantes. L'objectif est de faciliter l'ajout de nouvelles fonctionnalités tout en pérennisant l'accès au service. Pour une plus longue discussion sur les concepts logiciels introduits dans Road2, on pourra se référer à la documentation [suivante](./developers/concepts.md). + +Actuellement, Road2 propose deux moteurs, OSRM et PGRouting, via une unique API REST. + + +```{toctree} +--- +caption: Road2 +maxdepth: 2 +numbered: true +--- +Configuration +Données +Production +Changelog +``` + +---- + +```{toctree} +--- +caption: Développement +maxdepth: 2 +numbered: true +--- +Développement +Fonctionnalités +Concepts +Modification +Versionning +Documentation +``` + +---- + +```{toctree} +--- +caption: Tests +maxdepth: 2 +numbered: true +--- +Tests +Tests unitaires +Tests fonctionnels +Tests intégration +Tests charges +``` + +---- + +```{toctree} +--- +caption: Images docker +maxdepth: 2 +numbered: true +--- +Présentation +Environnement développement +Environnement demonstration +Distribution debian +Environnement tests +Image pour serveur web +``` diff --git a/test/functional/readme.md b/documentation/test/functional/readme.md similarity index 100% rename from test/functional/readme.md rename to documentation/test/functional/readme.md diff --git a/test/integration/readme.md b/documentation/test/integration/readme.md similarity index 96% rename from test/integration/readme.md rename to documentation/test/integration/readme.md index 011fd25..f063aea 100644 --- a/test/integration/readme.md +++ b/documentation/test/integration/readme.md @@ -42,36 +42,36 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - resource (resourceOperation) - portion (point, step, duration, distance) -Quatrième niveau: +- Quatrième niveau: - operationManager (parameterManager, operation, resourceOperation, log4js) - osrmResource (resource, resourceOperation) - pgrResource (resource, resourceOperation, log4js) - route (line, portion, duration, distance) -Cinquième niveau: +- Cinquième niveau: - routeResponse (response, point, route) -Sixième niveau: +- Sixième niveau: - osrmSource (source, osrm, routeResponse, nearestResponse, route, portion, line, point, step, distance, duration, errorManager, log4js) - pgrSource (source, routeResponse, isochroneResponse, route, portion, line, point, polygon, step, distance, duration, errorManager, gisManager, copyManager, simplify, turf, looseConstraint, log4js) -Septième niveau: +- Septième niveau: - sourceManager (osrmSource, pgrSource, errorManager, storageManager, operationManager, log4js) -Huitième niveau: +- Huitième niveau: - resourceManager (osrmResource, pgrResource, sourceManager, operationManager, log4js) -Neuvième niveau: +- Neuvième niveau: - service (apisManager, resourceManager, sourceManager, operationManager, baseManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) -Dixième niveau: +- Dixième niveau: - serviceManager (service, serviceProcess, log4js) - serviceProcess (serviceAdministered, service, log4js, fork) -Onzième niveau: +- Onzième niveau: - administrator (express, log4js, helmet, path, fs, assert, serverManager, serviceManager, apisManager) -Autres: +- Autres: - road2.js - controller.js de l'api simple 1.0.0 - index.js de l'api simple 1.0.0 diff --git a/test/load/readme.md b/documentation/test/load/readme.md similarity index 100% rename from test/load/readme.md rename to documentation/test/load/readme.md diff --git a/test/readme.md b/documentation/test/readme.md similarity index 100% rename from test/readme.md rename to documentation/test/readme.md diff --git a/test/unit/readme.md b/documentation/test/unit/readme.md similarity index 92% rename from test/unit/readme.md rename to documentation/test/unit/readme.md index f8dc270..6962e59 100644 --- a/test/unit/readme.md +++ b/documentation/test/unit/readme.md @@ -11,7 +11,7 @@ Mais cela devrait fonctionner uniquement avec `mocha`. Lancer la commande suivan mocha --recursive './test/unit/mocha/**/*.js' ``` -Les tests unitaires concernent les classes qui ne dépendent pas d'une autre classe du projet pour fonctionner. Les autres classes sont testées dans les tests d'intégration [ici](./integration/readme.md). +Les tests unitaires concernent les classes qui ne dépendent pas d'une autre classe du projet pour fonctionner. Les autres classes sont testées dans les tests d'intégration [ici](../integration/readme.md). On trouvera donc les classes ou les fichiers suivants: - api (ExpressJS, log4js) diff --git a/requirements/documentation.txt b/requirements/documentation.txt new file mode 100644 index 0000000..9469609 --- /dev/null +++ b/requirements/documentation.txt @@ -0,0 +1,8 @@ +# Documentation (for devs) +# ----------------------- + +myst-parser[linkify]>=0.17,<0.19 +sphinx-autobuild==2021.* +sphinx-book-theme<1 +sphinx-copybutton>=0.2,<1 +sphinxext-opengraph>=0.4,<1 From dae2d718cbc897ff52e3341e962010ee717c7e4a Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Tue, 17 Jan 2023 10:33:09 +0100 Subject: [PATCH 51/93] feat(doc) update root readme.md --- documentation/index.md | 2 +- readme.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/index.md b/documentation/index.md index 56e1c96..1f4eeb4 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -6,7 +6,7 @@ Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascri Road2 a été conçu dans l'idée de pouvoir facilement ajouter des nouveaux moteurs et de nouvelles APIs, et cela, de manière totalement transparente les uns pour autres. Autrement dit, ajouter un moteur n'a pas d'impact sur les APIs déjà existantes. L'objectif est de faciliter l'ajout de nouvelles fonctionnalités tout en pérennisant l'accès au service. Pour une plus longue discussion sur les concepts logiciels introduits dans Road2, on pourra se référer à la documentation [suivante](./developers/concepts.md). -Actuellement, Road2 propose deux moteurs, OSRM et PGRouting, via une unique API REST. +Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une unique API REST. ```{toctree} diff --git a/readme.md b/readme.md index 5b4c809..b4383bc 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascri Road2 a été conçu dans l'idée de pouvoir facilement ajouter des nouveaux moteurs et de nouvelles APIs, et cela, de manière totalement transparente les uns pour autres. Autrement dit, ajouter un moteur n'a pas d'impact sur les APIs déjà existantes. L'objectif est de faciliter l'ajout de nouvelles fonctionnalités tout en pérennisant l'accès au service. Pour une plus longue discussion sur les concepts logiciels introduits dans Road2, on pourra se référer à la documentation [suivante](./documentation/developers/concepts.md). -Actuellement, Road2 propose deux moteurs, OSRM et PGRouting, via une unique API REST. +Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une unique API REST. ## Fonctionnalités disponibles @@ -28,7 +28,7 @@ Road2 est diffusé sous la licence GPL v3. L'IGN propose un démonstrateur pour [l'itinéraire](https://geoservices.ign.fr/documentation/services_betas/itineraires.html) et [l'isochrone](https://geoservices.ign.fr/documentation/services_betas/isochrones.html). Ces démonstrateurs permettent de construire des requêtes via une carte et de visualiser les résultats. -Autrement, pour une première prise en main du service en local, il est possible d'utiliser l'image [alpine](./docker/demonstration/Dockerfile) de Road2. Cela permettra d'avoir localement une instance du service et une page web permettant de le tester. Les instructions de mise en place sont données [ici](./docker/demonstration/readme.md). +Autrement, pour une première prise en main du service en local, il est possible d'utiliser l'image [alpine](./docker/demonstration/Dockerfile) de Road2. Cela permettra d'avoir localement une instance du service et une page web permettant de le tester. Les instructions de mise en place sont données [ici](./documentation/docker/demonstration/readme.md). ### Découvrir et tester les APIs du service @@ -38,7 +38,7 @@ L'IGN propose également des pages pour tester une instance du service sur l'ens Autrement, l'ensemble des APIs disponibles sont documentées dans ce [dossier](./documentation/apis/). Pour le moment, il y a une seule API utilisateur qui est documentée via un [fichier](./documentation/apis/simple/1.0.0/api.yaml) YAML utilisant openapi 3.0.0, et une API d'administration documentée via un autre [fichier](./documentation/apis/administration/1.0.0/api.yaml) YAML suivant le même formalisme. -Il est possible de visualiser ces documentations d'API localement en suivant les instructions qui sont [ici](./docker/demonstration/readme.md). +Il est possible de visualiser ces documentations d'API localement en suivant les instructions qui sont [ici](./documentation/docker/demonstration/readme.md). ## Installation et utilisation de Road2 @@ -78,7 +78,7 @@ node ${road2}/src/js/road2.js --ROAD2_CONF_FILE=${configuration}/administration. ### Pour plus de détails -On trouvera dans le dossier [docker/distrubutions](./docker/distributions) différents Dockerfiles qui permettent de voir l'installation et de tester le service sur différentes plateformes. Pour le moment, Centos 7 et Debian 10 sont disponibles. +On trouvera dans le dossier [docker/distrubutions](./docker/distributions) différents Dockerfiles qui permettent de voir l'installation et de tester le service sur différentes plateformes. Pour le moment, Debian 10 est disponible. ## Participer aux développements @@ -88,7 +88,7 @@ On trouvera une documentation dédiée aux développeurs [ici](./documentation/d Pour en savoir plus sur notre roadmap, vous pouvez regarder le projet [IGNF/Road2 Roadmap](https://github.com/orgs/IGNF/projects/3). -Enfin, il est possible d'utiliser ce [docker-compose](./docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. +Enfin, il est possible d'utiliser ce [docker-compose](./documentation/docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. ## Utilisation en production From 3aea36d352f9a8a3022e9b9ab857b02cec5f8874 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Tue, 17 Jan 2023 11:15:34 +0100 Subject: [PATCH 52/93] feat(doc) update build documentation procedure --- documentation/.gitignore | 1 + documentation/developers/documentation.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/documentation/.gitignore b/documentation/.gitignore index ce4701d..e6b9593 100644 --- a/documentation/.gitignore +++ b/documentation/.gitignore @@ -1 +1,2 @@ code/ +_build diff --git a/documentation/developers/documentation.md b/documentation/developers/documentation.md index b3e9e5b..4209829 100644 --- a/documentation/developers/documentation.md +++ b/documentation/developers/documentation.md @@ -10,9 +10,9 @@ Pour la génération: # install aditionnal dependencies python -m pip install -U -r requirements/documentation.txt # build it -sphinx-build -b html docs docs/_build +sphinx-build -b html documentation documentation/_build # optimized (quiet, multiprocessing, doctrees separated) -sphinx-build -b html -d docs/_build/cache -j auto -q docs docs/_build/html +sphinx-build -b html -d documentation/_build/cache -j auto -q documentation documentation/_build/html ``` Ouvrir `docs/_build/index.html` dans un navigateur web. @@ -20,7 +20,7 @@ Ouvrir `docs/_build/index.html` dans un navigateur web. ## Ecrire la documentation avec un rendu en direct ```bash -sphinx-autobuild -b html -d docs/_build/cache docs/ docs/_build +sphinx-autobuild -b html -d documentation/_build/cache documentation/ documentation/_build ``` Ouvrir dans un navigateur web pour voir le rendu HTML mis à jour quand un fichier est sauvegardé. From 80857469c151394df5d6b7dccddb281a76930bc2 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Tue, 17 Jan 2023 11:16:48 +0100 Subject: [PATCH 53/93] feat(doc): replace O by 0 in changelog --- changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 2b9c867..25b9d80 100644 --- a/changelog.md +++ b/changelog.md @@ -47,15 +47,15 @@ FIXED: ADDED: - Ajout de la fonctionnalité nearest via OSRM -## 1.O.14 +## 1.0.14 FIXED: - Mauvaise ligne pour un log -## 1.O.13 +## 1.0.13 ADDED: - Pas d'erreur si certificat auto-signé ou périmé -## 1.O.12 +## 1.0.12 FIXED: - got dans bundledDependencies - http-proxy-agent dans bundledDependencies From 06e1b504111e64cdc0ff1d305342ce0cc8d0b75a Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 18 Jan 2023 10:41:48 +0100 Subject: [PATCH 54/93] feat(doc): use furo theme --- documentation/conf.py | 11 ++--------- documentation/configuration/readme.md | 2 +- documentation/index.md | 8 ++++---- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/documentation/conf.py b/documentation/conf.py index 18bf869..409e725 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -63,15 +63,8 @@ #html_favicon = str(__about__.__icon_path__) #html_logo = str(__about__.__icon_path__) -html_static_path = ["_static"] -html_theme = "sphinx_book_theme" -html_theme_options = { - "home_page_in_toc": True, - "path_to_docs": "docs", - "repository_branch": "develop", - "show_toc_level": 3, - -} +html_theme = "furo" +html_title = "Road2 documentation" # -- EXTENSIONS -------------------------------------------------------- # MyST Parser diff --git a/documentation/configuration/readme.md b/documentation/configuration/readme.md index 3352638..a165cc6 100644 --- a/documentation/configuration/readme.md +++ b/documentation/configuration/readme.md @@ -23,7 +23,7 @@ Chaque *service.json* va indiquer les éléments suivants : Ce fichier indique quelques informations générales liées à l'instance d'administration. Son principal objectif est l'indication des logs et des services gérés. -On peut trouver un [exemple](../../docker/config/road2.json) de ce fichier et le [modèle](./administration/administration_model.yaml) au format YAML. +On peut trouver un [exemple](../../docker/config/road2.json) de ce fichier et le [modèle](./administration/admin_model.yaml) au format YAML. ## service.json diff --git a/documentation/index.md b/documentation/index.md index 1f4eeb4..bcd700f 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -12,7 +12,7 @@ Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une ```{toctree} --- caption: Road2 -maxdepth: 2 +maxdepth: 1 numbered: true --- Configuration @@ -26,7 +26,7 @@ Changelog ```{toctree} --- caption: Développement -maxdepth: 2 +maxdepth: 1 numbered: true --- Développement @@ -42,7 +42,7 @@ Documentation ```{toctree} --- caption: Tests -maxdepth: 2 +maxdepth: 1 numbered: true --- Tests @@ -57,7 +57,7 @@ Tests charges ```{toctree} --- caption: Images docker -maxdepth: 2 +maxdepth: 1 numbered: true --- Présentation From 690dceda48e2ce15cd4bbf2c76c8cae973011d99 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 18 Jan 2023 10:57:44 +0100 Subject: [PATCH 55/93] fix(doc) forgot furo package in requirements --- requirements/documentation.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/documentation.txt b/requirements/documentation.txt index 9469609..5a6434e 100644 --- a/requirements/documentation.txt +++ b/requirements/documentation.txt @@ -1,8 +1,8 @@ # Documentation (for devs) # ----------------------- +furo==2022.* myst-parser[linkify]>=0.17,<0.19 sphinx-autobuild==2021.* -sphinx-book-theme<1 sphinx-copybutton>=0.2,<1 sphinxext-opengraph>=0.4,<1 From 996a6bda74b4293611c55036b48527f64b7db4f0 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 18 Jan 2023 11:27:16 +0100 Subject: [PATCH 56/93] fix(doc): fix documentation build path --- documentation/developers/documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/developers/documentation.md b/documentation/developers/documentation.md index 4209829..ace6bc9 100644 --- a/documentation/developers/documentation.md +++ b/documentation/developers/documentation.md @@ -15,7 +15,7 @@ sphinx-build -b html documentation documentation/_build sphinx-build -b html -d documentation/_build/cache -j auto -q documentation documentation/_build/html ``` -Ouvrir `docs/_build/index.html` dans un navigateur web. +Ouvrir `documentation/_build/index.html` dans un navigateur web. ## Ecrire la documentation avec un rendu en direct From a98f71222116fae77e976b764eb5a3f8401b210d Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Tue, 17 Jan 2023 14:06:43 +0100 Subject: [PATCH 57/93] feat(ci): docker hub image publication --- .github/workflows/docker_publish.yml | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/docker_publish.yml diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml new file mode 100644 index 0000000..6f4ed06 --- /dev/null +++ b/.github/workflows/docker_publish.yml @@ -0,0 +1,31 @@ +name: Docker Hub publish + +on: + push: + branches: + - "develop" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - + name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + file: ./docker/distributions/debian/Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/road2:latest From d520421d5bd77b94662907556b7dabaf99f83569 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Fri, 20 Jan 2023 09:32:43 +0100 Subject: [PATCH 58/93] feat(ci) use github container registry --- .github/workflows/docker_publish.yml | 73 +++++++++++++++++++++------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 6f4ed06..2e1e7e1 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -1,31 +1,70 @@ -name: Docker Hub publish +name: Docker GitHub Container Registry + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. on: + #schedule: + # - cron: '40 18 * * *' push: - branches: - - "develop" + branches: [ "develop" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "develop" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + jobs: build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: - - - name: Checkout + - name: Checkout repository uses: actions/checkout@v3 - - - name: Login to Docker Hub + + # Workaround: https://github.com/docker/build-push-action/issues/461 + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v2 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build and push + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push uses: docker/build-push-action@v3 with: context: . - file: ./docker/distributions/debian/Dockerfile - push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/road2:latest + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From 76df21e081ec784f174b903335ac6b7280175e19 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Fri, 20 Jan 2023 09:59:31 +0100 Subject: [PATCH 59/93] fix(ci) : invalid dockerfile path --- .github/workflows/docker_publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 2e1e7e1..2b6f29c 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -63,6 +63,7 @@ jobs: uses: docker/build-push-action@v3 with: context: . + file: ./docker/distributions/debian/Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From d6488e1aa48c362f2cde21f5634c3bc9d447c45f Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Mon, 23 Jan 2023 11:47:58 +0100 Subject: [PATCH 60/93] feat(ci): add documentation publish on github pages --- .github/workflows/documentation.yml | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/documentation.yml diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..75e02b2 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,63 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["develop"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +env: + PYTHON_VERSION: "3.10" + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + cache: "pip" + cache-dependency-path: "requirements/*.txt" + + - name: Install documentation requirements + run: | + python -m pip install -U -r requirements/documentation.txt + + - name: Build doc using Sphinx + run: sphinx-build -b html -j auto -d documentation/_build/cache -q documentation documentation/_build/html + + + - name: Setup Pages + uses: actions/configure-pages@v2 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: 'documentation/_build/html' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 From f5d443ce36457b289d3c699ba37e44ac37ef20ad Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Mon, 23 Jan 2023 14:50:30 +0100 Subject: [PATCH 61/93] doc(define_repo): define repository and use myst substitution --- documentation/conf.py | 30 ++++++++++++++++---- documentation/configuration/readme.md | 4 +-- documentation/developers/concepts.md | 2 +- documentation/developers/functionnalities.md | 2 +- documentation/docker/readme.md | 2 +- documentation/docker/test/readme.md | 2 +- documentation/index.md | 8 +++++- documentation/test/load/readme.md | 2 +- 8 files changed, 38 insertions(+), 14 deletions(-) diff --git a/documentation/conf.py b/documentation/conf.py index 409e725..ab89f09 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -6,6 +6,7 @@ # standard import sys +import json from datetime import datetime from os import environ, path @@ -14,6 +15,17 @@ # -- Build environment ----------------------------------------------------- on_rtd = environ.get("READTHEDOCS", None) == "True" +# -- Project information ----------------------------------------------------- +f = open('../package.json') + +package_node = json.load(f) + +author = package_node["author"] +description = package_node["description"] +project = package_node["name"] +version = package_node["version"] +uri_repository = "https://github.com/IGNF/road2/" + # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -66,6 +78,12 @@ html_theme = "furo" html_title = "Road2 documentation" +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +html_search_language = "fr" + # -- EXTENSIONS -------------------------------------------------------- # MyST Parser myst_enable_extensions = [ @@ -81,13 +99,13 @@ ] myst_substitutions = { - #"author": author, + "author": author, "date_update": datetime.now().strftime("%d %B %Y"), - #"description": description, - #"repo_url": __about__.__uri__, - #"title": project, - #"version": version, - #"license": license, + "description": description, + "repo_branch": "develop", + "repo_url": uri_repository, + "title": project, + "version": version, } myst_url_schemes = ["http", "https", "mailto"] diff --git a/documentation/configuration/readme.md b/documentation/configuration/readme.md index a165cc6..1b56eb9 100644 --- a/documentation/configuration/readme.md +++ b/documentation/configuration/readme.md @@ -55,13 +55,13 @@ On peut trouver un [exemple](../../docker/config/projections/projection.json) de Dans les fichiers du type *service.json*, il est possible d'indiquer plusieurs dossiers *sources*. Chaque dossier sera lu et les fichiers `*.source` seront analysés par Road2. Chacun de ces fichiers représente une source pour Road2. -On peut trouver, dans ce [dossier](./sources/), un exemple de ce genre de fichier pour chaque type de source disponible dans le code de Road2. +On peut trouver, dans {{ '[documentation/configuration/sources]({}/tree/{}/documentation/configuration/sources)'.format(repo_url, repo_branch) }}, un exemple de ce genre de fichier pour chaque type de source disponible dans le code de Road2. ## Les ressources Dans les fichiers *service.json*, il peut également indiquer plusieurs dossiers *resources*. Chaque dossier sera lu et les fichiers `*.resource` seront analysés par Road2. Chacun de ces fichiers représente cette fois-ci une ressource pour Road2. -On peut trouver, dans ce [dossier](./resources/), un exemple de ce genre de fichier pour chaque type de ressource disponible dans le code de Road2. Chaque type suit le même modèle YAML. +On peut trouver, dans ce {{ '[documentation/configuration/resources]({}/tree/{}/documentation/configuration/resources)'.format(repo_url, repo_branch) }}, un exemple de ce genre de fichier pour chaque type de ressource disponible dans le code de Road2. Chaque type suit le même modèle YAML. ## Les fichiers liés à certains moteurs de Road2 diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index ac141ad..6382872 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -14,7 +14,7 @@ C'est le concept de base pour comprendre le code de Road2. Par exemple, si on considère une API qui s'appelle `rest` qui ne possède qu'une seule version `1.0.0`. Dans cette API, on pourrait définir une seule route `compute` qui permet de demander un itinéraire avec les paramètres `start` et `end` au minimum. On parlera alors de l'API `rest/1.0.0` qui permet à un utilisateur d'obtenir un itinéraire en faisant la requête `/rest/1.0.0/compute?start=2,48&end=2,48.1`. -Chaque API est définie dans un dossier distinct des autres. Cela les rend indépendantes les unes des autres. Et pour une même appellation, on a une indépendance entre deux versions différentes. On trouvera des exemples d'implémentation dans le dossier des [apis du code](../../src/js/apis/). +Chaque API est définie dans un dossier distinct des autres. Cela les rend indépendantes les unes des autres. Et pour une même appellation, on a une indépendance entre deux versions différentes. On trouvera des exemples d'implémentation dans le dossier des {{ '[apis du code]({}/tree/{}/src/js/apis)'.format(repo_url, repo_branch) }}. #### Notion de moteur diff --git a/documentation/developers/functionnalities.md b/documentation/developers/functionnalities.md index e4c8433..9539dfa 100644 --- a/documentation/developers/functionnalities.md +++ b/documentation/developers/functionnalities.md @@ -174,4 +174,4 @@ PGRouting gère tout les types de contraintes, des plus simples au plus complexe Le GetCapabilities est une réponse JSON qui décrit les paramètres de chaque opérations et les valeurs disponibles pour une instance de Road2. -On trouvera un exemple dans la [documentation](../apis/simple/1.0.0/). \ No newline at end of file +On trouvera un exemple dans la {{ '[documentation]({}/tree/{}/documentation/apis/simple/1.0.0/)'.format(repo_url, repo_branch) }}. \ No newline at end of file diff --git a/documentation/docker/readme.md b/documentation/docker/readme.md index c7478cb..fffb1b4 100644 --- a/documentation/docker/readme.md +++ b/documentation/docker/readme.md @@ -9,5 +9,5 @@ Il y a un sous-dossier pour les grands usages identifiés : D'autres sous-dossiers sont ordonnés ainsi pour des raisons pratiques : - [web](./web/readme.md) : Ce dossier regroupe des fichiers utiles pour avoir un petit site web qui contient plusieurs documentations et des pages de tests graphiques pour Road2. -- [config](./config/) : Ce dossier regroupe plusieurs fichiers de configurations qui se trouvent être communs aux autres sous-dossiers. +- {{ '[config]({}/tree/{}/docker/config/)'.format(repo_url, repo_branch) }} : Ce dossier regroupe plusieurs fichiers de configurations qui se trouvent être communs aux autres sous-dossiers. - [distributions](./distributions/readme.md) : Ce dossier peut regrouper différents `Dockerfile` qui sont des exemples d'installation sous différentes distributions. Actuellement, il ne reste plus qu'un exemple pour Debian. C'est le Dockerfile préconisé pour développer sur Road2 car il contient tous les binaires utiles aux différents moteurs. \ No newline at end of file diff --git a/documentation/docker/test/readme.md b/documentation/docker/test/readme.md index 71f0c8a..2771e6d 100644 --- a/documentation/docker/test/readme.md +++ b/documentation/docker/test/readme.md @@ -19,7 +19,7 @@ Il suffit de lancer la commande `docker-compose build`. ### Tests de charge avec gatling -Après avoir rempli le `.env` en pointant, par exemple, sur le `user-files` de ce [dépôt](../../test/load/gatling/user-files) et en ayant pris soin de choisir un scénario, il suffit d'exécuter la commande : +Après avoir rempli le `.env` en pointant, par exemple, sur le `user-files` de ce {{ '[dépôt]({}/tree/{}/test/load/gatling/user-files/)'.format(repo_url, repo_branch) }} et en ayant pris soin de choisir un scénario, il suffit d'exécuter la commande : ``` # Une fois le .env modifié pour choisir le scénario notamment # Choisir le scénario (attention road2Docker ne fonctionne pas tant que docker-compose up generate-load-data n'a pas été appelé au moins une fois) diff --git a/documentation/index.md b/documentation/index.md index bcd700f..9a4de84 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -1,4 +1,10 @@ -# Road2 +# {{ title }} - Documentation + +> **Description:** {{ description }} +> **Auteur et contributeurs:** {{ author }} +> **Version:** {{ version }} +> **Code source:** {{ repo_url }} +> **Dernière mise à jour de la documentation:** {{ date_update }} ## Présentation générale diff --git a/documentation/test/load/readme.md b/documentation/test/load/readme.md index f4ab26d..7e336a5 100644 --- a/documentation/test/load/readme.md +++ b/documentation/test/load/readme.md @@ -10,7 +10,7 @@ Si Gatling est installé sur la machine, on pourra pointer le dossier `user-file Autrement, il est possible d'utiliser l'image docker disponible sur [dockerhub](https://hub.docker.com/r/denvazh/gatling). -C'est ce qui est fait dans le [docker-compose](../../docker/test/) dédié aux tests dans ce dépôt. Voir le [readme](../../docker/test/readme.md) pour son utilisation. +C'est ce qui est fait dans le {{ '[docker-compose]({}/tree/{}/docker/test/)'.format(repo_url, repo_branch) }} dédié aux tests dans ce dépôt. Voir le [readme](../../docker/test/readme.md) pour son utilisation. ## random-route-generator From 9c54314e03d0f507f08553201d28f6d69045a478 Mon Sep 17 00:00:00 2001 From: lgrd Date: Mon, 30 Jan 2023 12:05:38 +0100 Subject: [PATCH 62/93] [feat] creation of class serviceInsider (#33) * [feat] creation of class serviceInsider * [doc] update changelog and concepts --- changelog.md | 3 +- documentation/developers/concepts.md | 4 +- src/js/administrator/administrator.js | 3 +- src/js/service/serviceAdministered.js | 12 ++ src/js/service/serviceInsider.js | 146 ++++++++++++++++++ src/js/service/serviceManager.js | 19 +-- src/js/service/serviceProcess.js | 4 +- .../service/integrationServiceProcess.js | 4 +- 8 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 src/js/service/serviceInsider.js diff --git a/changelog.md b/changelog.md index 25b9d80..097cb07 100644 --- a/changelog.md +++ b/changelog.md @@ -4,7 +4,8 @@ ADDED: - La classe Administrator permet de gérer le service via une API. Notamment la création, la suppression et la modification d'un service seront possible. - - Cette classe est configurée par un nouveau fichier de configuration. + - Cette classe est configurée par un nouveau fichier de configuration. + - Les classes service* sont des interfaces pour permettre à l'administrateur de gérer les services associés. Elles permettent de gérer un service dans le même processus ou dans un nouveau (méthode conseillée). - Ajout du moteur Valhalla pour les itinéraires et les isochrones - Le module `wkt` a été remplacé par une implémentation interne - Le format wkt est disponible pour le paramètre geometryFormat de l'API simple/1.0.0 diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index 6382872..f1db7d5 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -132,7 +132,9 @@ Cet administrateur permet plusieurs choses : Un administrateur a été créé pour réaliser des tâches qui auraient gêné la bonne exécution du service. -L'administrateur a donc été créé pour être indépendant du service. Si l'administrateur a des tâches fastidieuses, cela n'impacte pas le service. Si l'un tombe, l'autre non. +L'administrateur a donc été créé pour être indépendant du service. Dans leur écriture, ils ont été pensé pour être lancé dans des processus différents. Ainsi, si l'administrateur a des tâches fastidieuses, cela n'impacte pas le service. Si l'un tombe, l'autre non. + +Cependant, il est possible de démarrer un service dans le même processus que son administrateur. #### Lancement d'un service diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 89bf1d2..c4f2f07 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -458,8 +458,9 @@ module.exports = class Administrator { } // Chargement du service + let options = {adminLogConfiguration: this._logConfiguration}; LOGGER.info("Chargement du service..."); - if (!this._serviceManager.loadService(curIASConf.creationType, curIASConf.id, serviceConfLocation, serviceConfiguration)) { + if (!await this._serviceManager.loadService(curIASConf.creationType, curIASConf.id, serviceConfLocation, options)) { LOGGER.error("Impossible de créer le service "+ curIASConf.id); // On essaye les suivants continue; diff --git a/src/js/service/serviceAdministered.js b/src/js/service/serviceAdministered.js index 4fea566..2789d8c 100644 --- a/src/js/service/serviceAdministered.js +++ b/src/js/service/serviceAdministered.js @@ -54,4 +54,16 @@ module.exports = class ServiceAdministered { return this._type; } + /** + * + * @function + * @name loadService + * @description Fonction pour utiliser pour charger un service selon le mode adaptée à la classe fille. Elle doit être ré-écrite dans chaque classe fille. + * + */ + loadService() { + + } + + } diff --git a/src/js/service/serviceInsider.js b/src/js/service/serviceInsider.js new file mode 100644 index 0000000..4505850 --- /dev/null +++ b/src/js/service/serviceInsider.js @@ -0,0 +1,146 @@ +'use strict'; + +const log4js = require('log4js'); +const fs = require('fs'); + +const ServiceAdministered = require('./serviceAdministered'); +const Service = require('./service'); + +// Création du LOGGER +const LOGGER = log4js.getLogger("SERVICEINS"); + +/** +* +* @class +* @name ServiceInsider +* @description Classe modélisant un service administré mais qui se situe dans un autre processus que celui de l'administrateur qui le gère. +* +*/ + +module.exports = class ServiceInsider extends ServiceAdministered { + + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe ServiceInsider + * @param {string} id - Identifiant du service pour l'administrateur + * @param {string} location - Localisation de la configuration du service + * + */ + constructor(id, location) { + + // Constructeur parent + super(id, "sameProcess"); + + // Emplacement de la configuration + this._configurationLocation = location; + + // Instance de la classe Service + this._serviceInstance = {}; + + } + + /** + * + * @function + * @name loadService + * @description Créer un service administré via une instanciation de la classe Service + * @param {object} options - Options pour charger le service (ici, il faut la configuration des logs de l'administrateur pour s'y rattacher) + * + * + */ + async loadService(options) { + + LOGGER.info("Création et lancement d'un service dans dans le même processus"); + + // Un minimum de vérifications au cas où + if (typeof(this._configurationLocation) !== "string") { + LOGGER.error("Le chemin fourni n'est pas une string"); + return null; + } else { + LOGGER.debug("Le chemin fourni est bien une string"); + } + + if (this._configurationLocation === "") { + LOGGER.error("Le chemin fourni est vide"); + return null; + } else { + LOGGER.debug("Le chemin est renseigné : " + this._configurationLocation); + } + + if (!options) { + LOGGER.error("Options vides"); + return null; + } + if (!options.adminLogConfiguration) { + LOGGER.error("Options sans attribut 'adminLogConfiguration'"); + return null; + } + + // Création du service via un fork : on lance simplement service/main.js + LOGGER.info("Instanciation d'un service..."); + + this._serviceInstance = new Service(); + + LOGGER.info("Instance créée"); + + LOGGER.info("Execution des taches pour démarrer le service..."); + + // Récupération de la configuration + let configuration = {}; + try { + configuration = JSON.parse(fs.readFileSync(this._configurationLocation)); + } catch(error) { + LOGGER.error("Impossible de lire la configuration : " + this._configurationLocation); + LOGGER.error(error); + return false; + } + + // Vérification au cas où cela n'a pas été fait avant + if (!(await this._serviceInstance.checkServiceConfiguration(configuration, this._configurationLocation))) { + LOGGER.error("Configuration non validée. Impossible de démarrer le service"); + return false; + } else { + + LOGGER.debug("Configuration du service vérifiée et suffisamment validée pour lancer un service"); + + // On aura besoin de cette configuration juste après et peut-être plus tard + this._serviceInstance.saveServiceConfiguration(configuration, this._configurationLocation, options.adminLogConfiguration); + + // On lance le chargement du service qui est normalement correctement configuré + if (!this._serviceInstance.loadServiceConfiguration()) { + LOGGER.error("Configuration non chargée. Impossible de démarrer le service"); + return false; + } else { + + LOGGER.debug("Service chargé"); + + // On connecte les sources + if (!(await this._serviceInstance.connectSources())) { + + LOGGER.error("Aucune source n'a pu être connectée"); + return false; + + } else { + + LOGGER.info("Les sources connectables ont été connectées"); + + // On démarre les serveurs associé à ce service + if (!this._serviceInstance.startServers()) { + LOGGER.error("Impossible de démarrer les serveurs. Impossible de démarrer le service"); + return false; + } + + } + + } + + } + + return true; + + } + +} diff --git a/src/js/service/serviceManager.js b/src/js/service/serviceManager.js index 7a93be4..443abad 100644 --- a/src/js/service/serviceManager.js +++ b/src/js/service/serviceManager.js @@ -2,6 +2,7 @@ const log4js = require('log4js'); const Service = require('./service'); +const ServiceInsider = require('./serviceInsider'); const ServiceProcess = require('./serviceProcess'); // Création du LOGGER @@ -29,9 +30,6 @@ module.exports = class serviceManager { // Catalogue de services chargés (objets de la classe ServiceAdministered) this._loadedServiceAdministeredCatalog = {}; - // Contenu de la configuration des services chargés (contenu du service.json) - this._loadedServiceConfigurations = {}; - // Emplacement de la configuration des services chargés (chemin du service.json) this._loadedServiceConfLocations = {}; @@ -73,10 +71,10 @@ module.exports = class serviceManager { * @param {string} creationType - Type de création pour le service. Permet d'indiquer si on est dans le même process ou pas, voir sur une autre machine en théorie. * @param {string} id - Id du service pour l'administrateur * @param {string} configurationLocation - Emplacement de la configuration du service à charger - * @param {object} serviceConfiguration - Contenu de la configuration du service à charger + * @param {object} options - Contenu optionnel pour le chargement de certains types de services * */ - loadService(creationType, id, configurationLocation, serviceConfiguration) { + async loadService(creationType, id, configurationLocation, options) { LOGGER.info("Création et lancement du service " + id); @@ -89,8 +87,9 @@ module.exports = class serviceManager { if (creationType === "sameProcess") { // Instanciation du service directement sur le même process que l'administrateur - LOGGER.error("Type de création non implémentée. Impossible de créer le service."); - return false; + LOGGER.debug("sameProcess : Création d'un service dans le même processus"); + + serviceAdministered = new ServiceInsider(id, configurationLocation); } else if (creationType === "newProcess") { @@ -115,12 +114,14 @@ module.exports = class serviceManager { // On le démarre par le même appel, qu'importe son type de création LOGGER.info("Démarrage du service " + id); - serviceAdministered.loadService(); + if (!(await serviceAdministered.loadService(options))) { + LOGGER.error("Impossible de charger le service " + id); + return false; + } // Sauvegarde du service dans le manager LOGGER.info("Service démarré. Sauvegarde dans le serviceManager..."); this._loadedServiceAdministeredCatalog[id] = serviceAdministered; - this._loadedServiceConfigurations[id] = serviceConfiguration; this._loadedServiceConfLocations[id] = configurationLocation; return true; diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js index 369bc20..ab8c978 100644 --- a/src/js/service/serviceProcess.js +++ b/src/js/service/serviceProcess.js @@ -37,7 +37,7 @@ module.exports = class ServiceProcess extends ServiceAdministered { this._configurationLocation = location; // Instance de childProcess quand le processus est lancé - this._serviceAdministered = {}; + this._serviceInstance = {}; } @@ -82,7 +82,7 @@ module.exports = class ServiceProcess extends ServiceAdministered { // Création du service via un fork : on lance simplement service/main.js LOGGER.info("Fork du processus pour créer le service..."); - this._serviceAdministered = fork("./src/js/service/main.js", [this._configurationLocation], serviceOptions); + this._serviceInstance = fork("./src/js/service/main.js", [this._configurationLocation], serviceOptions); LOGGER.info("Processus enfant créé"); diff --git a/test/integration/mocha/service/integrationServiceProcess.js b/test/integration/mocha/service/integrationServiceProcess.js index ff0b749..448c2e7 100644 --- a/test/integration/mocha/service/integrationServiceProcess.js +++ b/test/integration/mocha/service/integrationServiceProcess.js @@ -27,8 +27,8 @@ describe('Test de la classe ServiceProcess', function() { assert.equal(serviceProcess._configurationLocation, configuration); }); - it('Get _serviceAdministered', function() { - assert.deepEqual(serviceProcess._serviceAdministered, {}); + it('Get _serviceInstance', function() { + assert.deepEqual(serviceProcess._serviceInstance, {}); }); }); From eb38d203887176570a2b56a8c745de0ae3359caf Mon Sep 17 00:00:00 2001 From: lgrd Date: Wed, 15 Feb 2023 16:56:29 +0100 Subject: [PATCH 63/93] [doc] update routes of admin api (#38) * [doc] nouvelles routes pour l'api admin * [doc] add object definitions * [doc] complete health parameters and response --- docker/config/service.json | 2 +- .../apis/administration/1.0.0/api.yaml | 1084 +++++++++++++++-- .../apis/administration/1.0.0/utilisation.md | 189 +++ 3 files changed, 1172 insertions(+), 103 deletions(-) create mode 100644 documentation/apis/administration/1.0.0/utilisation.md diff --git a/docker/config/service.json b/docker/config/service.json index 265db84..b3b4c68 100644 --- a/docker/config/service.json +++ b/docker/config/service.json @@ -7,7 +7,7 @@ "provider": { "name": "IGN", "site": "www.ign.fr", - "mail": "sav@ign.fr" + "mail": "contact.geoservices@ign.fr" }, "logs": { "configuration": "/home/docker/config/log4js-service.json" diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index 10a9a6f..a0ef73e 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -1,23 +1,38 @@ openapi: "3.0.0" + info: - description: "Description de l'API d'administration du service d'itinéraire." + description: "Description de l'API d'administration de Road2." version: "1.0.0" - title: "Administration du Service d'itinéraire" + title: "Administration de Road2" contact: - email: "rdev@ign.fr" + email: "contact.geoservices@ign.fr" + servers: - - url: "https://localhost:8080/admin/1.0.0/" + - url: "https://localhost:8079/admin/1.0.0/" description: "Serveur de test local" + tags: -- name: "Administration" - description: "Administrer le service d'itinéraire" +- name: "État du serveur" + description: "Connaître l'état de l'instance globale (administrateur et services)" +- name: "Gestion de l'administrateur" + description: "Administrer l'administrateur" +- name: "Gestion des services" + description: "Administrer des services" +- name: "Gestion des ressources" + description: "Administrer les ressources sur un service spécifique." +- name: "Gestion des sources" + description: "Administrer les sources sur un service spécifique." + paths: + /version: get: tags: - - "Administration" + - "État du serveur" summary: "Obtenir la version du serveur." - description: "Cette requête retourne la version du serveur. " + description: | + Cette requête retourne la version de Road2 utilisée par cette instance + de l'administrateur. operationId: "version" responses: 200: @@ -29,13 +44,29 @@ paths: properties: version: type: "string" + description: "Version de Road2 utilisée" + /health: get: tags: - - "Administration" + - "État du serveur" summary: "Obtenir l'état du serveur." - description: "Cette requête retourne l'état du serveur. " + description: | + Cette requête retourne l'état du serveur d'administration et + de l'ensemble des services qu'il administre. Cet état fait principalement + référence à la disponibilité des données. Chaque service a un état lié à la + disponibilité de ses sources. Chaque source a un état lié à la disponibilité de ses données. operationId: "health" + parameters: + - name: "verbose" + description: | + "Par défaut, puisque verbose=false, la requête retourne seulement l'état global + de l'administrateur et de ses services. Si on souhaite récupérer plus d'information + sur l'état de chaque service, on met verbose à true." + in: "query" + required: false + schema: + type: "boolean" responses: 200: description: "successful operation" @@ -46,74 +77,315 @@ paths: properties: state: type: "string" - /server: + enum: ["green","yellow","red"] + description: | + "Lorsque tout va bien: 'green'. Si un des services a + un problème:yellow. Si aucun service n'est disponible:red. Seule information retournée + si verbose=false. " + example: "green" + administrator: + type: "object" + properties: + state: + type: "string" + example: "green" + enum: ["green","yellow","red"] + description: | + "Pour le moment, seul green est disponible car soit le serveur est disponible pour + répondre aux requêtes d'administration, soit il ne l'est pas du tout." + services: + type: "array" + items: + type: "object" + properties: + id: + type: "string" + example: "main" + description: | + "Id du service pour l'administrateur" + state: + type: "string" + example: "green" + enum: ["green","yellow","red"] + description: | + "Lorsque toutes les sources sont disponibles : 'green'. Si une des sources a + un problème:yellow. Si aucune source n'est disponible:red." + sources: + type: "array" + items: + type: "object" + properties: + id: + type: "string" + example: "bduni-idf-car-fastest" + description: | + "Id de la source pour le service" + state: + type: "string" + example: "green" + enum: ["green","red"] + description: | + "Lorsque les données sont disponibles : 'green'. Si les données ne le sont plus: 'red'." + + /configuration: get: tags: - - "Administration" - summary: "Obtenir la configuration du server." - description: "Cette requête retourne la configuration du server. " - operationId: "server" + - "Gestion de l'administrateur" + summary: "Obtenir la configuration de l'administrateur." + description: | + Cette requête retourne la configuration de l'administrateur. + operationId: "get-admin-configuration" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/adminConfiguration" + patch: + tags: + - "Gestion de l'administrateur" + summary: "Modifier la configuration de l'administrateur." + description: | + Dans la configuration de l'administrateur, tout est modifiable. + Cela permet notamment de créer, modifier et supprimer les services + gérés par cet administrateur. + operationId: "patch-admin-configuration" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/adminConfiguration" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/adminConfiguration" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + + /services: + get: + tags: + - "Gestion des services" + summary: "Obtenir la liste des services proposés par l'administrateur" + description: | + Cette requête retourne l'ensemble des services connus par l'administrateur. + Cette liste peut être vide si aucun service n'a été configuré. + operationId: "services" + parameters: + - name: "withId" + description: "Si on souhaite récupérer les ids des services. Ces ids sont normalement dans la configuration de l'administrateur. " + in: "query" + required: false + schema: + type: "boolean" responses: 200: + description: "successful operation" + content: + application/json: + schema: + type: "array" + items: + type: "object" + allOf: + - type: "object" + properties: + id: + type: "string" + - $ref: "#/components/schemas/serviceConfiguration" + post: + tags: + - "Gestion des services" + summary: "Créer un service." + description: | + Si un service n'existe pas, il est possible de le créer + en fournissant sa configuration. + operationId: "post-service" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/serviceConfiguration" + responses: + 201: description: "successful operation" content: application/json: schema: type: "object" - properties: - configFile: - type: "string" - configuration: - type: "object" - 403: - description: "Not allowed" + allOf: + - type: "object" + properties: + id: + type: "string" + - $ref: "#/components/schemas/serviceConfiguration" + 400: + description: "Invalid parameters" content: application/json: schema: $ref: "#/components/schemas/errorResponse" - /resource: + + /services/{service}: get: tags: - - "Administration" - summary: "Obtenir la configuration d'une ressource." - description: "Cette requête retourne la configuration d'une ressource." - operationId: "get-resource" + - "Gestion des services" + summary: "Obtenir la configuration d'un service." + description: | + Cette requête retourne la configuration du service demandé. + operationId: "get-service" parameters: - - name: "id" - in: "query" - description: "Tableau des ids de ressources à décrire. Chaque id peut être complet ou incomplet. S'il est complet, seule cette ressource sera décrite. Si aucune ressource ne correspond, alors toutes les ressources pouvant correspondre vont être décrite. Équivalent du LIKE en SQL." + - name: "service" + description: "Id du service concerné" + in: "path" required: true schema: - type: "array" - items: - type: "string" - explode: false - style: "pipeDelimited" - example: "bduni" + type: "string" responses: 200: description: "successful operation" content: application/json: schema: - type: "array" - items: - type: "object" - properties: - resourceId: - type: "string" - configFile: - type: "string" - configuration: - type: "object" + $ref: "#/components/schemas/serviceConfiguration" 400: description: "Invalid parameters" content: application/json: schema: $ref: "#/components/schemas/errorResponse" - 403: - description: "Not allowed" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + patch: + tags: + - "Gestion des services" + summary: "Modifier la configuration d'un service existant." + description: | + Il s'agit de modifier des paramètres de configuration d'un service + déjà existant. Il faut fournir la configuration du service à modifier. + Si un redémarrage du service est nécessaire, il sera de fait + temporairement indisponible. + operationId: "patch-service" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/serviceConfiguration" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/serviceConfiguration" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + delete: + tags: + - "Gestion des services" + summary: "Supprimer un service." + description: | + Pour supprimer un service, il sera nécessaire d'avoir d'abord + supprimé les sources et ressources associées. Une fois supprimés, + un service et sa configuration ne peuvent être récupérés. + operationId: "delete-service" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + responses: + 204: + description: "successful operation" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + + /services/{service}/resources: + get: + tags: + - "Gestion des ressources" + summary: "Obtenir la liste des ressources proposées par un service." + description: | + Cette requête retourne la liste des ressources sur le service indiqué. + operationId: "get-resources" + parameters: + - name: "service" + in: "path" + description: "Id du service concerné" + required: true + schema: + type: "string" + - name: "source" + description: | + Filtre pour avoir les ressources qui utilisent cette source + in: "query" + required: false + schema: + type: "string" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + type: "object" + properties: + resources: + type: "array" + items: + $ref: "#/components/schemas/resourceConfiguration" + 400: + description: "Invalid parameters" content: application/json: schema: @@ -126,36 +398,72 @@ paths: $ref: "#/components/schemas/errorResponse" post: tags: - - "Administration" - summary: "Modifier une ressource existante. Il faut fournir la configuration de la ressource à modifier." - description: "" + - "Gestion des ressources" + summary: "Intégrer une nouvelle ressource. Il faut fournir la configuration de la ressource à intégrer." + description: "Les sources indiquées doivent déjà exister." operationId: "post-resource" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/createResource" + type: "object" responses: - 200: + 201: description: "successful operation" content: application/json: schema: type: "object" - properties: - configFile: - type: "string" - configuration: - type: "object" 400: description: "Invalid parameters" content: application/json: schema: $ref: "#/components/schemas/errorResponse" - 403: - description: "Not allowed" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + + /services/{service}/resources/{resource}: + get: + tags: + - "Gestion des ressources" + summary: "Obtenir la configuration d'une ressource." + description: "Cette requête retourne la configuration d'une ressource." + operationId: "get-resource" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + - name: "resource" + in: "path" + description: "Id de la ressource concernée" + required: true + schema: + type: "string" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/resourceConfiguration" + 400: + description: "Invalid parameters" content: application/json: schema: @@ -166,18 +474,99 @@ paths: application/json: schema: $ref: "#/components/schemas/errorResponse" - put: + patch: tags: - - "Administration" - summary: "Intégrer une nouvelle ressource. Il faut fournir la configuration de la ressource à intégrer." - description: "" - operationId: "put-resource" + - "Gestion des ressources" + summary: "Modifier une ressource existante. Il faut fournir la configuration de la ressource à modifier." + description: "Les sources indiquées doivent déjà exister." + operationId: "patch-resource" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + - name: "resource" + in: "path" + description: "Id de la ressource concernée" + required: true + schema: + type: "string" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/createResource" + $ref: "#/components/schemas/resourceConfiguration" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/resourceConfiguration" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + delete: + tags: + - "Gestion des ressources" + summary: "Supprimer une ressource. Il faut fournir l'id de la ressource à supprimer." + description: "" + operationId: "delete-resource" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + - name: "resource" + in: "path" + description: "Id de la ressource concernée" + required: true + schema: + type: "string" + responses: + 204: + description: "successful operation" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + + /services/{service}/sources: + get: + tags: + - "Gestion des sources" + summary: "Obtenir la liste des sources proposées par un service." + description: "Cette requête retourne la liste des sources sur le service indiqué." + operationId: "get-sources" + parameters: + - name: "service" + in: "path" + description: "Id du service concerné" + required: true + schema: + type: "string" responses: 200: description: "successful operation" @@ -186,18 +575,78 @@ paths: schema: type: "object" properties: - configFile: - type: "string" - configuration: - type: "object" + sources: + type: "array" + items: + $ref: "#/components/schemas/sourceConfiguration" + post: + tags: + - "Gestion des sources" + summary: "Intégrer une nouvelle source. Il faut fournir la configuration de la source à intégrer." + description: "La donnée doit déjà exister." + operationId: "post-source" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/sourceConfiguration" + responses: + 201: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/sourceConfiguration" 400: description: "Invalid parameters" content: application/json: schema: $ref: "#/components/schemas/errorResponse" - 403: - description: "Not allowed" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + + /services/{service}/sources/{source}: + get: + tags: + - "Gestion des sources" + summary: "Obtenir la configuration d'une source." + description: "Cette requête retourne la configuration d'une source déjà existante." + operationId: "get-source" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + - name: "source" + in: "path" + description: "Id de la source concernée" + required: true + schema: + type: "string" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/sourceConfiguration" + 400: + description: "Invalid parameters" content: application/json: schema: @@ -208,44 +657,76 @@ paths: application/json: schema: $ref: "#/components/schemas/errorResponse" - delete: + patch: tags: - - "Administration" - summary: "Intégrer une nouvelle ressource. Il faut fournir l'id des ressources à supprimer." - description: "" - operationId: "delete-resource" + - "Gestion des sources" + summary: "Modifier une source existante. Il faut fournir la configuration de la source à modifier." + description: "La donnée doit déjà exister." + operationId: "patch-source" parameters: - - name: "id" - in: "query" - description: "Tableau des ids de ressources à supprimer. Les ids doivent être complets." + - name: "service" + description: "Id du service concerné" + in: "path" required: true schema: - type: "array" - items: - type: "string" - explode: false - style: "pipeDelimited" - example: "bduni" + type: "string" + - name: "source" + in: "path" + description: "Id de la source concernée" + required: true + schema: + type: "string" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/sourceConfiguration" responses: 200: description: "successful operation" content: application/json: schema: - type: "array" - items: - type: "object" - properties: - resourceId: - type: "string" + $ref: "#/components/schemas/sourceConfiguration" 400: description: "Invalid parameters" content: application/json: schema: $ref: "#/components/schemas/errorResponse" - 403: - description: "Not allowed" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + delete: + tags: + - "Gestion des sources" + summary: "Surpprimer une source. Il faut fournir l'id de la source à supprimer." + description: | + La suppression de la donnée doit être faite autrement. + On doit avoir supprimé son usage dans les ressources avant. + operationId: "delete-source" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + - name: "source" + in: "path" + description: "Id de la source concernée" + required: true + schema: + type: "string" + responses: + 204: + description: "successful operation" + 400: + description: "Invalid parameters" content: application/json: schema: @@ -256,16 +737,10 @@ paths: application/json: schema: $ref: "#/components/schemas/errorResponse" + components: schemas: - createResource: - type: "object" - properties: - resource: - type: "object" - properties: - file: - type: "string" + errorResponse: type: "object" properties: @@ -276,3 +751,408 @@ components: type: "string" message: type: "string" + + adminConfiguration: + type: "object" + properties: + administration: + type: "object" + properties: + api: + type: "object" + properties: + name: + type: "string" + example: "admin" + version: + type: "string" + example: "1.0.0" + services: + type: "array" + minItems: 0 + items: + type: "object" + properties: + id: + type: "string" + example: "main" + configuration: + type: "string" + example: "/home/docker/config/service.json" + onStart: + type: "string" + example: "true" + creationType: + type: "string" + example: "newProcess" + network: + type: "object" + properties: + server: + type: "object" + properties: + id: + type: "string" + example: "administrator" + https: + type: "string" + example: "false" + host: + type: "string" + example: "0.0.0.0" + port: + type: "string" + example: "8079" + logs: + type: "object" + properties: + configuration: + type: "string" + example: "/home/docker/config/log4js-administration.json" + + serviceConfiguration: + type: "object" + properties: + application: + type: "object" + properties: + name: + type: "string" + example: "Road2" + title: + type: "string" + example: "Service de calcul d'itinéraire" + description: + type: "string" + example: "Ce service permet de calculer des itinéraires sur les données du Géoportail." + url: + type: "string" + example: "https://localhost/" + provider: + type: "object" + properties: + name: + type: "string" + example: "IGN" + site: + type: "string" + example: "www.ign.fr" + mail: + type: "string" + example: "contact.geoservices@ign.fr" + logs: + type: "object" + properties: + configuration: + type: "string" + example: "/home/docker/config/log4js-service.json" + operations: + type: "object" + properties: + directory: + type: "string" + example: "/home/docker/app/src/resources/operations" + parameters: + type: "object" + properties: + directory: + type: "string" + example: "/home/docker/app/src/resources/parameters" + resources: + type: "object" + properties: + directories: + type: "array" + items: + type: "string" + example: "/home/docker/data/resources/" + sources: + type: "object" + properties: + directories: + type: "array" + items: + type: "string" + example: "/home/docker/data/sources/" + network: + type: "object" + properties: + servers: + type: "array" + items: + type: "object" + properties: + id: + type: "string" + example: "internalServer" + https: + type: "string" + example: "false" + host: + type: "string" + example: 0.0.0.0 + port: + type: "string" + example: "8080" + cors: + type: "object" + properties: + configuration: + type: "string" + example: "/home/docker/config/cors.json" + projections: + type: "object" + properties: + directory: + type: "string" + example: "/home/docker/config/projections/" + apis: + type: "array" + items: + type: "object" + properties: + name: + type: "string" + example: "simple" + version: + type: "string" + example: "1.0.0" + + resourceConfiguration: + type: "object" + properties: + resource: + type: "object" + properties: + id: + type: "string" + example: "my-resource" + type: + type: "string" + example: "osrm" + description: + type: "string" + example: "Exemple d'une ressource." + resourceVersion: + type: "string" + example: "yyyy-mm-dd" + sources: + type: "array" + items: + type: "string" + example: "data-car-fastest" + availableOperations: + type: "array" + items: + type: "object" + properties: + id: + type: "string" + example: "route" + parameters: + type: "array" + items: + type: "object" + properties: + id: + type: "string" + example: "resource" + defaultValueContent: + type: "string" + values: + type: "object" + + sourceConfiguration: + oneOf: + - $ref: "#/components/schemas/osrmSourceConfiguration" + - $ref: "#/components/schemas/pgrSourceConfiguration" + - $ref: "#/components/schemas/valhallaSourceConfiguration" + + osrmSourceConfiguration: + type: "object" + properties: + id: + type: "string" + example: "bduni-idf-car-fastest" + description: + type: "string" + example: "test osrm" + type: + type: "string" + enum: ["osrm"] + projection: + type: "string" + example: "EPSG:4326" + bbox: + type: "string" + example: "1.748.43.349.1" + storage: + type: "object" + properties: + file: + type: "string" + example: "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" + cost: + type: "object" + properties: + cost: + type: "object" + properties: + profile: + type: "string" + example: "car" + optimization: + type: "string" + example: "fastest" + + pgrSourceConfiguration: + type: "object" + properties: + id: + type: "string" + example: "bduni-idf-car-fastest" + description: + type: "string" + example: "test osrm" + type: + type: "string" + enum: ["pgrouting"] + projection: + type: "string" + example: "EPSG:4326" + bbox: + type: "string" + example: "1.748.43.349.1" + storage: + type: "object" + properties: + base: + type: "object" + properties: + dbConfig: + type: "string" + example: "/home/docker/data/output_base.json" + schema: + type: "string" + example: "public" + attributes: + type: "array" + items: + type: "object" + properties: + key: + type: "string" + example: "name" + column: + type: "string" + example: "way_names" + default: + type: "string" + example: "false" + costs: + type: "array" + items: + type: "object" + properties: + cost: + type: "object" + properties: + profile: + type: "string" + example: "car" + optimization: + type: "string" + example: "fastest" + costType: + type: "string" + example: "time" + costColumn: + type: "string" + example: "cost_s_car" + rcostColumn: + type: "string" + example: "reverse_cost_s_car" + + valhallaSourceConfiguration: + type: "object" + properties: + id: + type: "string" + example: "bduni-idf-car-fastest" + description: + type: "string" + example: "test osrm" + type: + type: "string" + enum: ["valhalla"] + projection: + type: "string" + example: "EPSG:4326" + bbox: + type: "string" + example: "1.748.43.349.1" + storage: + oneOf: + - type: "object" + properties: + file: + type: "string" + example: "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" + - type: "object" + properties: + base: + type: "object" + properties: + dbConfig: + type: "string" + example: "/home/docker/data/output_base.json" + schema: + type: "string" + example: "public" + attributes: + type: "array" + items: + type: "object" + properties: + key: + type: "string" + example: "name" + column: + type: "string" + example: "way_names" + default: + type: "string" + example: "false" + - type: "object" + properties: + tar: + type: "string" + example: "/home/docker/data/bdtopo-valhalla-tiles.tar" + dir: + type: "string" + example: "/home/docker/data/bdtopo-valhalla-tiles/" + config: + type: "string" + example: "/home/docker/data/valhalla.json" + costs: + type: "array" + items: + type: "object" + properties: + cost: + type: "object" + properties: + profile: + type: "string" + example: "car" + optimization: + type: "string" + example: "fastest" + costType: + type: "string" + example: "time" + costing: + type: "string" + example: "auto" + + diff --git a/documentation/apis/administration/1.0.0/utilisation.md b/documentation/apis/administration/1.0.0/utilisation.md new file mode 100644 index 0000000..1affd81 --- /dev/null +++ b/documentation/apis/administration/1.0.0/utilisation.md @@ -0,0 +1,189 @@ +# Utilisation de l'API d'administration + +## Concepts + +Cette API d'administration a été codée dans l'idée de permettre le plus d'actions possibles sur la configuration de Road2. Le deuxième objectif a été de fournir quelques raccourcis et fonctionnalités supplémentaires jugées utiles et qui ne complexifiaient pas beaucoup le code. + +Les concepts utiles (administrateur, services, ressources et sources) sont définis sur la page des [concepts](../../../developers/concepts.md). + + + + +## Prérequis + +Afin de pouvoir utiliser cette API, il est nécessaire que l'administrateur soit lancé avec une configuration d'administrateur valide (ex. [road2.json](../../../../docker/config/road2.json)). Par contre, il n'est pas nécessaire d'avoir configuré des services, des sources et des ressources. Ils pourront l'être par la suite. + +L'autre prérequis, notamment afin de pouvoir créer un service, sera d'avoir des données accessibles par Road2. En effet, l'administrateur n'a pas vocation à gérer les données elles-mêmes (ex. création/suppression). + + + + + +## Fonctionnalités + +### Récupérer la configuration de l'administrateur + +La configuration existe nécessairement. Elle est donc renvoyée. + +### Modifier la configuration de l'administrateur + +Dans la configuration de l'administrateur, tout est modifiable. Cela permet notamment de créer, modifier et supprimer les services gérés par cet administrateur. + +Quand une modification est envoyée, la nouvelle configuration résultante est d'abord analysée. Si elle est valide, alors le changement est pris en compte. Sinon, une erreur est renvoyée. + +Lorsque l'on modifie la configuration, l'administrateur prend tout de suite en compte ces modifications. Si un redémarrage est nécessaire, alors l'administrateur sera temporairement indisponible. Selon le mode de création des services qui en dépendent, il y aura ou pas d'interruption de ces derniers. + + + + +### Gérer un service + +Si un service existe, il est possible de le modifier et de le supprimer. S'il n'existe pas, on peut le créer. + +#### Récupérer la configuration d'un service + +Si la configuration existe, alors elle est renvoyée. Sinon, une erreur est renvoyée. + +#### Modifier la configuration d'un service + +Pour modifier la configuration d'un service, celle-ci doit exister. On peut alors tout modifier. + +Quand une modification est envoyée, la nouvelle configuration résultante est d'abord analysée. +Si elle est valide, alors le changement peut être pris en compte. Sinon, un code d'erreur est renvoyé. +Si un redémarrage du service est nécessaire, il sera alors temporairement indisponible. + +#### Créer un service + +Si un service n'existe pas, il est possible de le créer en fournissant sa configuration. + +La configuration est d'abord vérifiée. Si elle est valide, le service est alors créé. Sinon, une erreur est renvoyée. + +Une fois créé, le service est disponible. + +#### Supprimer un service + +Pour supprimer un service, il sera nécessaire d'avoir d'abord supprimé les sources et ressources associées. + +Une fois supprimés, un service et sa configuration ne peuvent être récupérés. + + + +### Gérer une source pour un service spécifique + +La gestion des sources se fait toujours au sein d'un service spécifique. + +L'accéssibilité d'une source se fait via la notion de ressource. Si aucune ressource n'utilise une source, cette dernière n'est donc pas interrogeable. Cependant, pour créer une ressource, il est nécessaire d'avoir d'abord créer au moins une source qu'elle utilisera. + +#### Obtenir la configuration d'une source + +Si une source existe, il est possible récupérer sa configuration. Si elle n'existe pas, une erreur est renvoyée. + +#### Modifier la configuration d'une source + +Pour modifier la configuration d'une source, celle-ci doit exister. On peut alors tout modifier. + +Quand une modification est envoyée, la nouvelle configuration résultante est d'abord analysée. +Si elle est valide, alors le changement peut être pris en compte. Sinon, un code d'erreur est renvoyé. + +#### Créer la configuration d'une source + +Créer la configuration d'une source revient à créer une source. + +Pour créer une source, seule la présence des données est également nécessaire. + + +#### Supprimer la configuration d'une source + +Supprimer la configuration d'une source revient à supprimer une source. + +Pour supprimer une source, il est nécessaire que son utilisation au sein des ressources soient déjà supprimée. + + + + +### Gérer une ressource pour un service spécifique + +La gestion des ressources se fait toujours au sein d'un service spécifique. + +#### Obtenir la configuration d'une ressource + +Si une ressource existe, il est possible récupérer sa configuration. Si elle n'existe pas, une erreur est renvoyée. + +#### Modifier la configuration d'une ressource + +Pour modifier la configuration d'une ressource, celle-ci doit exister. On peut alors tout modifier. + +Quand une modification est envoyée, la nouvelle configuration résultante est d'abord analysée. +Si elle est valide, alors le changement peut être pris en compte. Sinon, un code d'erreur est renvoyé. + +#### Créer la configuration d'une ressource + +Créer la configuration d'une ressource revient à créer une ressource. Quand la configuration est créée, la ressource est rendue disponible. + +Pour créer une ressource, les sources utilisées doivent exister. + +#### Supprimer la configuration d'une ressource + +Supprimer la configuration d'une ressource revient à supprimer une ressource. La ressource n'est donc plus disponible. + + + + + +### Connaître l'état de l'administrateur et des services associés + +Cet état fait référence à la disponibilité des données. Chaque service a un état lié à la disponibilité de ses ressources. Chaque ressource a un état lié à la dispoinibilité de ses sources. Et chaque source, celle de ses données. + + + + + +### Connaître la version du serveur déployée + +Connaitre la version de Road2 déployée sur l'instance en cours. + + + + + +## Raccourcis + +### Lister les services gérés par un administrateur + +Pour certains usages, on souhaitera connaitre la liste des services disponibles. Il serait possible d'avoir cette information en lisant la configuration de l'administrateur. + +### Lister les ressources proposées par un service + +Pour certains usages, on souhaitera connaitre la liste des ressources disponibles sur un service spécifique. + +### Lister les sources proposées par un service + +Pour certains usages, on souhaitera connaitre la liste des sources disponibles sur un service spécifique. + + + + + +## Exemples d'utilisation + +Les cas les plus fréquents semblent être les cas où l'on a un administrateur et des services qui sont déjà bien configurés. On souhaite alors simplement administrer cette configuration existante. Pour cela, il suffira de se référer au paragraphe décrivant les fonctionnalités. + +### Configurer un service uniquement via l'API d'administration + +Dans certains cas, on voudra créer un nouveau service à côté de ceux déjà existants ou tout simplement créer le tout premier. Voici comment faire : + +En supposant que l'administrateur soit bien configuré et démarré, et que des données soient disponibles, on peut configurer entièrement un nouveau service via l'API d'administration. + +On va commencer par créer un service avec une bonne configuration. + +Maintenant donc, on va pouvoir créer des sources en utilisant les données déjà disponibles. + +À partir de ces sources, on va pouvoir créer des ressources. + +Tout est en place, il ne reste plus qu'à rendre le service disponible. + + + + + + From 192801ba9550e33fcd9955d73304a52c15aa5b1b Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 8 Mar 2023 16:41:27 +0100 Subject: [PATCH 64/93] fix(docker): limit conan version for valhalla build --- docker/distributions/debian/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index fde30e6..5fb8053 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -14,7 +14,7 @@ RUN git clone --depth 1 --recursive https://github.com/kevinkreiser/prime_server cmake -B build . && cmake --build build && make -C build install WORKDIR /home/valhalla/ -RUN pip install --upgrade conan +RUN pip install --upgrade "conan<2.0.0" RUN git clone --branch 3.2.0-with_hard_exclude --depth 1 --recursive https://github.com/IGNF/valhalla.git && cd valhalla && \ mkdir build && cmake -B build -DCMAKE_BUILD_TYPE=Release && make -C build && make -C build package @@ -47,7 +47,7 @@ COPY src ./src/ COPY *.json ./ ### Installation des dépendances de l'application NodeJS -RUN npm install && npm install -g mocha eslint jsdoc +RUN npm install && npm install -g mocha eslint jsdoc nyc ### Volume partagé pour lire les données VOLUME ["/home/docker/data"] From 43d8f56ff3999e410e3c5663f403a0bea149985c Mon Sep 17 00:00:00 2001 From: lgrd Date: Thu, 9 Mar 2023 10:14:06 +0100 Subject: [PATCH 65/93] Development of health route (#39) * wip: creation of health objects/algo * [test]: maj de request * wip: it works for sameProcess services * works for newProcesses * add type inside response, update tests and doc * [fix] delete read message * [doc] fix changelog version --- changelog.md | 6 + docker/dev/docker-compose.yml | 1 + documentation/test/integration/readme.md | 3 + src/js/administrator/administrator.js | 109 ++++++++++++++ .../apis/admin/1.0.0/controller/controller.js | 83 +++++++++++ src/js/apis/admin/1.0.0/index.js | 31 +++- src/js/requests/healthRequest.js | 54 +++++++ src/js/requests/isochroneRequest.js | 28 +++- src/js/requests/nearestRequest.js | 28 +++- src/js/requests/request.js | 29 +--- src/js/requests/routeRequest.js | 28 +++- src/js/responses/healthResponse.js | 109 ++++++++++++++ src/js/responses/isochroneResponse.js | 28 +++- src/js/responses/nearestResponse.js | 28 +++- src/js/responses/response.js | 24 +--- src/js/responses/routeResponse.js | 28 +++- src/js/service/main.js | 6 + src/js/service/service.js | 135 +++++++++++++++++- src/js/service/serviceAdministered.js | 11 ++ src/js/service/serviceInsider.js | 17 +++ src/js/service/serviceManager.js | 48 +++++++ src/js/service/serviceProcess.js | 107 +++++++++++++- src/js/sources/source.js | 8 +- src/js/sources/sourceManager.js | 29 ++-- .../cucumber/features/support/world.js | 1 + .../requests/integrationHealthRequest.js | 31 ++++ .../responses/integrationHealthResponse.js | 49 +++++++ test/unit/mocha/requests/testsRequest.js | 11 +- test/unit/mocha/responses/testsResponse.js | 12 +- 29 files changed, 994 insertions(+), 88 deletions(-) create mode 100644 src/js/apis/admin/1.0.0/controller/controller.js create mode 100644 src/js/requests/healthRequest.js create mode 100644 src/js/responses/healthResponse.js create mode 100644 test/integration/mocha/requests/integrationHealthRequest.js create mode 100644 test/integration/mocha/responses/integrationHealthResponse.js diff --git a/changelog.md b/changelog.md index 097cb07..0ad95e8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.1.0 + +CHANGED: + - La documentation de l'API d'administration a été grandement enrichie. + - La route /health a une réponse plus complète et est vraiment codée pour prendre en compte l'état de chaque service et chaque source disponibles. + ## 2.0.0 ADDED: diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index a7869ec..9a5d4e7 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -14,6 +14,7 @@ services: command : "npm run debug -- --ROAD2_CONF_FILE=../config/road2.json" ports: - 8080:8080 + - 8079:8079 - 9229:9229 - 9230:9230 - 443:443 diff --git a/documentation/test/integration/readme.md b/documentation/test/integration/readme.md index f063aea..53f2372 100644 --- a/documentation/test/integration/readme.md +++ b/documentation/test/integration/readme.md @@ -20,6 +20,8 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - operation (parameter) - resourceParameter (parameter) - serverManager (server, ExpressJS, log4js, fs, assert) + - healthRequest (request) + - healthResponse (response) - Deuxième niveau: - routeRequest (request, point) @@ -123,6 +125,7 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - Router() - router.use() - json() + - set - got - utils/httpQuery.js diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index c4f2f07..5a79ed0 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -9,6 +9,7 @@ const assert = require('assert').strict; const serverManager = require('../server/serverManager'); const serviceManager = require('../service/serviceManager'); const apisManager = require('../apis/apisManager'); +const HealthResponse = require('../responses/healthResponse'); // Création du LOGGER const LOGGER = log4js.getLogger("ADMINISTRATOR"); @@ -324,6 +325,9 @@ module.exports = class Administrator { // Application Express let administrator = express(); + // Stockage de l'instance Administrator dans l'app expressJS afin que les informations soient accessibles par les requêtes + administrator.set("administrator", this); + // Gestion des en-têtes avec helmet selon les préconisations d'ExpressJS administrator.use(helmet()); @@ -474,6 +478,111 @@ module.exports = class Administrator { } + /** + * + * @function + * @name computeHealthRequest + * @description Gestion de la requête d'état du serveur + * Cette fonction ne suit pas le m$eme chemin que les autres car elle est globale + * et ne concerne pas un service particulier + * Elle renvoit une healthResponse complète et c'est au niveau de l'API qu'on peut la modifier + * @param {HealthRequest} healthRequest - Instance de la classe HealthRequest + * + */ + + async computeHealthRequest(healthRequest) { + + LOGGER.info("computeHealthRequest..."); + + let gotOrange = false; + let gotRed = false; + + let healthResponse = new HealthResponse(); + + // Étant donné que l'administrateur est en train de répondre, on le met au vert + // À voir s'il y a des fonctionnalités qui pourraient le mettre à l'orange ou au rouge + // Dans ce cas là, il faudra que l'administrator ait un attribut d'état et que celui-ci soit lu dans cette fonction + healthResponse.adminState = "green"; + + // Pour chaque service, on demande au serviceManager l'état du service + for (let i = 0; i < this._configuration.administration.services.length; i++) { + + let curServiceId = this._configuration.administration.services[i].id; + LOGGER.debug("Demande de l'état du service : " + curServiceId); + + // Le passage potentiel par IPC fait perdre les méthodes donc dans la suite, on est obligé de prendre les attributs avec _ + let curHealthResponse = await this._serviceManager.computeRequest(curServiceId, healthRequest); + + if (curHealthResponse._type !== "healthResponse") { + // Ce n'est pas normal, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse du bon type"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } + + if (!curHealthResponse._serviceStates[0]) { + // Ce n'est pas normal, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } + + if (!curHealthResponse._serviceStates[0].state) { + // Ce n'est pas normal, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " n'a pas donné d'état"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } + + // Pour la suite, on note la présence d'orange et de rouge dans les services + if (curHealthResponse._serviceStates[0].state === "orange") { + LOGGER.debug("Le service " + curServiceId + " est orange"); + gotOrange = true; + } else if (curHealthResponse._serviceStates[0].state === "red") { + LOGGER.debug("Le service " + curServiceId + " est red"); + gotRed = true; + } else if (curHealthResponse._serviceStates[0].state === "green") { + // Tout va bien, rien à faire + LOGGER.debug("Le service " + curServiceId + " est green"); + } else { + // Cela ne devrait pas arriver, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " est dans un état inconnu"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } + + // On stocke le retour de ce service + curHealthResponse._serviceStates[0].id = curServiceId; + healthResponse.serviceStates.push(curHealthResponse._serviceStates[0]); + + } + + // En fonction des états définis précédemment, on va définir l'état global + // Pour faire simple : + // - global est orange si un des services est orange + // - global est rouge si un des services est rouge + if (gotRed) { + healthResponse.globalState = "red"; + } else { + if (gotOrange) { + healthResponse.globalState = "orange"; + } else { + healthResponse.globalState = "green"; + } + } + + return healthResponse; + + } + } diff --git a/src/js/apis/admin/1.0.0/controller/controller.js b/src/js/apis/admin/1.0.0/controller/controller.js new file mode 100644 index 0000000..41eddac --- /dev/null +++ b/src/js/apis/admin/1.0.0/controller/controller.js @@ -0,0 +1,83 @@ +'use strict'; + +const errorManager = require('../../../../utils/errorManager'); +const log4js = require('log4js'); +const HealthRequest = require('../../../../requests/healthRequest'); + +var LOGGER = log4js.getLogger("CONTROLLER"); + +module.exports = { + + /** + * + * @function + * @name checkHealthParameters + * @description Vérification des paramètres d'une requête sur /health + * @param {object} parameters - ensemble des paramètres de la requête + * @return {object} HealthRequest - Instance de la classe HealthRequest + * + */ + + checkHealthParameters: function(parameters) { + + LOGGER.debug("checkHealthParameters()"); + + // Il n'y a aucun paramètre obligatoire donc on peut créer l'objet request + let healthRequest = new HealthRequest(); + + // Verbose + if (parameters.verbose) { + + LOGGER.debug("user verbose:"); + LOGGER.debug(parameters.verbose); + + if (parameters.verbose === "true") { + healthRequest.verbose = true; + } else if (parameters.verbose === "false") { + healthRequest.verbose = false; + } else { + throw errorManager.createError(" Parameter 'verbose' is invalid: value should be 'true' or 'false'", 400); + } + + } else { + // paramètre non obligatoire + LOGGER.debug("default verbose used: false"); + } + + return healthRequest; + + }, + + /** + * + * @function + * @name writeHealthResponse + * @description Ré-écriture de la réponse pour une requête sur /health + * @param {object} HealthRequest - Instance de la classe HealthRequest + * @param {object} HealthResponse - Instance de la classe HealthResponse + * @return {object} userResponse - Réponse envoyée à l'utilisateur + * + */ + + writeHealthResponse: function(healthRequest, healthResponse) { + + let userResponse = {}; + + LOGGER.debug("writeHealthResponse()"); + + userResponse.state = healthResponse.globalState; + + if (!healthRequest.verbose) { + return userResponse; + } + + userResponse.administrator = {}; + userResponse.administrator.state = healthResponse.adminState; + + userResponse.services = healthResponse.serviceStates; + + return userResponse; + + } + +} \ No newline at end of file diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index ef879b3..9cc5829 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -4,6 +4,7 @@ const express = require('express'); const log4js = require('log4js'); const packageJSON = require('../../../../../package.json'); +const controller = require('./controller/controller'); var LOGGER = log4js.getLogger("ADMIN"); var router = express.Router(); @@ -32,7 +33,6 @@ router.route("/version") // Health // Pour avoir l'état du service -// TODO: implémenter une véritable vérification de l'état router.route("/health") .get(async function(req, res, next) { @@ -40,10 +40,31 @@ router.route("/health") LOGGER.debug("requete GET sur /admin/1.0.0/health?"); LOGGER.debug(req.originalUrl); - res.set('content-type', 'application/json'); - res.status(200).json({ - "state": "green" - }); + // On récupère l'instance d'Administrator pour répondre aux requêtes + let administrator = req.app.get("administrator"); + + // on récupère l'ensemble des paramètres de la requête + let parameters = req.query; + LOGGER.debug(parameters); + + try { + + // Vérification des paramètres de la requête + const healthRequest = controller.checkHealthParameters(parameters); + LOGGER.debug(healthRequest); + // Envoie au service et récupération de l'objet réponse + const healthResponse = await administrator.computeHealthRequest(healthRequest); + LOGGER.debug(healthResponse); + // Formattage de la réponse + const userResponse = controller.writeHealthResponse(healthRequest, healthResponse); + LOGGER.debug(userResponse); + + res.set('content-type', 'application/json'); + res.status(200).json(userResponse); + + } catch (error) { + return next(error); + } }); diff --git a/src/js/requests/healthRequest.js b/src/js/requests/healthRequest.js new file mode 100644 index 0000000..edcba9a --- /dev/null +++ b/src/js/requests/healthRequest.js @@ -0,0 +1,54 @@ +'use strict'; + +const Request = require('./request'); + +/** +* +* @class +* @name healthRequest +* @description Classe modélisant une requête d'état du serveur Road2. +* +*/ + +module.exports = class healthRequest extends Request { + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe healthRequest + * + */ + + constructor() { + + super("health","healthRequest"); + + this._verbose = false; + + } + + /** + * + * @function + * @name get verbose + * @description Récupérer le caractère verbeux de la requête + * + */ + get verbose() { + return this._verbose; + } + + /** + * + * @function + * @name set verbose + * @description Attribuer le caractère verbeux de la requête. + * @param {Boolean} + * + */ + set verbose(v) { + this._verbose = v; + } + +} diff --git a/src/js/requests/isochroneRequest.js b/src/js/requests/isochroneRequest.js index 62e248b..e4ed838 100644 --- a/src/js/requests/isochroneRequest.js +++ b/src/js/requests/isochroneRequest.js @@ -35,7 +35,10 @@ module.exports = class isochroneRequest extends Request { constructor(resource, point, costType, costValue, profile, direction, askedProjection, geometryFormat, timeUnit, distanceUnit) { // Constructeur parent - super("isochrone", resource, "isochroneRequest"); + super("isochrone", "isochroneRequest"); + + // Ressource concernée + this._resource = resource; // Initialisation du reste des paramètres this._point = point; @@ -61,6 +64,29 @@ module.exports = class isochroneRequest extends Request { } + /** + * + * @function + * @name get resource + * @description Récupérer la ressource de la requête + * + */ + get resource () { + return this._resource; + } + + /** + * + * @function + * @name set resource + * @description Attribuer la ressource de la requête + * @param {string} res - Id de la ressource + * + */ + set resource (res) { + this._resource = res; + } + /** * * @function diff --git a/src/js/requests/nearestRequest.js b/src/js/requests/nearestRequest.js index b640d83..e0aebea 100644 --- a/src/js/requests/nearestRequest.js +++ b/src/js/requests/nearestRequest.js @@ -28,7 +28,10 @@ module.exports = class nearestRequest extends Request { constructor(resource, coordinates) { // Constructeur parent - super("nearest", resource, "nearestRequest"); + super("nearest", "nearestRequest"); + + // Ressource concernée + this._resource = resource; // Coordonnées du point fourni this._coordinates = coordinates; @@ -38,6 +41,29 @@ module.exports = class nearestRequest extends Request { } + /** + * + * @function + * @name get resource + * @description Récupérer la ressource de la requête + * + */ + get resource () { + return this._resource; + } + + /** + * + * @function + * @name set resource + * @description Attribuer la ressource de la requête + * @param {string} res - Id de la ressource + * + */ + set resource (res) { + this._resource = res; + } + /** * * @function diff --git a/src/js/requests/request.js b/src/js/requests/request.js index 107952f..83e9356 100644 --- a/src/js/requests/request.js +++ b/src/js/requests/request.js @@ -18,46 +18,19 @@ module.exports = class Request { * @name constructor * @description Constructeur de la classe Request * @param {string} operation - Type d'opération concernée - * @param {string} resource - Ressource concernée * @param {string} type - Type de la requête * */ - constructor(operation, resource, type) { + constructor(operation, type) { // Opération concernée this._operation = operation; - // Ressource concernée - this._resource = resource; - // Type de la requête (ne doit pas être modifié) this._type = type; } - /** - * - * @function - * @name get resource - * @description Récupérer la ressource de la requête - * - */ - get resource () { - return this._resource; - } - - /** - * - * @function - * @name set resource - * @description Attribuer la ressource de la requête - * @param {string} res - Id de la ressource - * - */ - set resource (res) { - this._resource = res; - } - /** * * @function diff --git a/src/js/requests/routeRequest.js b/src/js/requests/routeRequest.js index c651ff9..3410036 100644 --- a/src/js/requests/routeRequest.js +++ b/src/js/requests/routeRequest.js @@ -29,7 +29,10 @@ module.exports = class routeRequest extends Request { constructor(resource, start, end, profile, optimization) { // Constructeur parent - super("route", resource, "routeRequest"); + super("route", "routeRequest"); + + // Ressource concernée + this._resource = resource; // Point de départ this._start = start; @@ -73,6 +76,29 @@ module.exports = class routeRequest extends Request { } + /** + * + * @function + * @name get resource + * @description Récupérer la ressource de la requête + * + */ + get resource () { + return this._resource; + } + + /** + * + * @function + * @name set resource + * @description Attribuer la ressource de la requête + * @param {string} res - Id de la ressource + * + */ + set resource (res) { + this._resource = res; + } + /** * * @function diff --git a/src/js/responses/healthResponse.js b/src/js/responses/healthResponse.js new file mode 100644 index 0000000..e3480a5 --- /dev/null +++ b/src/js/responses/healthResponse.js @@ -0,0 +1,109 @@ +'use strict'; + +const Response = require('./response'); + +/** +* +* @class +* @name healthResponse +* @description Classe modélisant une réponse de l'état du serveur (health). +* +*/ + +module.exports = class healthResponse extends Response { + + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe healthResponse + * + */ + constructor() { + + // Type de la réponse + super("healthResponse"); + + // État global + this._globalState = "unknown"; + + // État de l'administrateur + this._adminState = "unknown"; + + // États des services + // Un administrateur peut n'avoir aucun service + this._serviceStates = new Array(); + + } + + /** + * + * @function + * @name get globalState + * @description Récupérer le globalState + * + */ + get globalState () { + return this._globalState; + } + + /** + * + * @function + * @name set globalState + * @description Attribuer le globalState + * @param {string} st - État + * + */ + set globalState (st) { + this._globalState = st; + } + + /** + * + * @function + * @name get adminState + * @description Récupérer le adminState + * + */ + get adminState () { + return this._adminState; + } + + /** + * + * @function + * @name set adminState + * @description Attribuer le adminState + * @param {string} st - État + * + */ + set adminState (st) { + this._adminState = st; + } + + /** + * + * @function + * @name get serviceStates + * @description Récupérer le serviceStates + * + */ + get serviceStates () { + return this._serviceStates; + } + + /** + * + * @function + * @name set serviceStates + * @description Attribuer le serviceStates + * @param {Array} st - États des services + * + */ + set serviceStates (st) { + this._serviceStates = st; + } + +} diff --git a/src/js/responses/isochroneResponse.js b/src/js/responses/isochroneResponse.js index 4261153..5d738c8 100644 --- a/src/js/responses/isochroneResponse.js +++ b/src/js/responses/isochroneResponse.js @@ -42,7 +42,10 @@ module.exports = class isochroneResponse extends Response { distanceUnit) { // Constructeur parent - super(resource); + super("isochroneResponse"); + + // Ressource + this._resource = resource; // point this._point = point; @@ -73,6 +76,29 @@ module.exports = class isochroneResponse extends Response { } + /** + * + * @function + * @name get resource + * @description Récupérer la ressource de la réponse + * + */ + get resource () { + return this._resource; + } + + /** + * + * @function + * @name set resource + * @description Attribuer la ressource de la réponse + * @param {Point} res - Ressource + * + */ + set resource (res) { + this._resource = res; + } + /** * * @function diff --git a/src/js/responses/nearestResponse.js b/src/js/responses/nearestResponse.js index 7a4f3d8..22fbe49 100644 --- a/src/js/responses/nearestResponse.js +++ b/src/js/responses/nearestResponse.js @@ -25,7 +25,10 @@ module.exports = class nearestResponse extends Response { constructor(resource, coordinates) { // Constructeur parent - super(resource); + super("nearestResponse"); + + // Ressource + this._resource = resource; // coordinates this._coordinates = coordinates; @@ -36,6 +39,29 @@ module.exports = class nearestResponse extends Response { } + /** + * + * @function + * @name get resource + * @description Récupérer la ressource de la réponse + * + */ + get resource () { + return this._resource; + } + + /** + * + * @function + * @name set resource + * @description Attribuer la ressource de la réponse + * @param {Point} res - Ressource + * + */ + set resource (res) { + this._resource = res; + } + /** * * @function diff --git a/src/js/responses/response.js b/src/js/responses/response.js index a85e55e..2568122 100644 --- a/src/js/responses/response.js +++ b/src/js/responses/response.js @@ -16,37 +16,25 @@ module.exports = class Response { * @function * @name constructor * @description Constructeur de la classe Response - * @param {string} resource - Nom de la ressource + * @param {string} type - Type de la réponse * */ - constructor(resource) { + constructor(type) { // Nom de la ressource concernée par la réponse - this._resource = resource; + this._type = type; } /** * * @function - * @name get resource + * @name get type * @description Récupérer la ressource de la requête * */ - get resource () { - return this._resource; - } - - /** - * - * @function - * @name set resource - * @description Attribuer la ressource de la requête - * @param {string} res - Nom de la ressource - * - */ - set resource (res) { - this._resource = res; + get type () { + return this._type; } diff --git a/src/js/responses/routeResponse.js b/src/js/responses/routeResponse.js index 088b76e..5bcea10 100644 --- a/src/js/responses/routeResponse.js +++ b/src/js/responses/routeResponse.js @@ -28,7 +28,10 @@ module.exports = class routeResponse extends Response { constructor(resource, start, end, profile, optimization) { // Constructeur parent - super(resource); + super("routeResponse"); + + // Ressource + this._resource = resource; // start this._start = start; @@ -48,6 +51,29 @@ module.exports = class routeResponse extends Response { } + /** + * + * @function + * @name get resource + * @description Récupérer la ressource de la réponse + * + */ + get resource () { + return this._resource; + } + + /** + * + * @function + * @name set resource + * @description Attribuer la ressource de la réponse + * @param {Point} res - Ressource + * + */ + set resource (res) { + this._resource = res; + } + /** * * @function diff --git a/src/js/service/main.js b/src/js/service/main.js index 4c0aec4..dbfa975 100644 --- a/src/js/service/main.js +++ b/src/js/service/main.js @@ -80,6 +80,12 @@ async function startService() { LOGGER.info("Les sources connectables ont été connectées"); + // Instanciation de la fonction permettant de recevoir des messages via IPC + if (process.argv[3] === "child") { + LOGGER.info("Ce service est un child d'un administrateur. Instanciation de la fonction permettant de recevoir les messages"); + service.initIPC(); + } + // On démarre les serveurs associé à ce service if (!service.startServers()) { pm.shutdown(5); diff --git a/src/js/service/service.js b/src/js/service/service.js index 78cfbf0..3c2b0a1 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -14,6 +14,8 @@ const ProjectionManager = require('../geography/projectionManager'); const ServerManager = require('../server/serverManager'); const LogManager = require('../utils/logManager'); const log4js = require('log4js'); +const errorManager = require('../utils/errorManager'); +const HealthResponse = require('../responses/healthResponse'); // Création du LOGGER const LOGGER = log4js.getLogger("SERVICE"); @@ -732,7 +734,7 @@ module.exports = class Service { } - if (this._sourceManager.source.length === 0) { + if (this._sourceManager.sources.length === 0) { LOGGER.fatal("Aucune ressource n'a pu etre chargee"); return false; } @@ -947,7 +949,7 @@ module.exports = class Service { let sourceId = resource.getSourceIdFromRequest(request); // La source est dans le catalogue du sourceManager - let source = this._sourceManager.source[sourceId]; + let source = this._sourceManager.sources[sourceId]; // --- //On renvoie la requête vers le moteur @@ -963,4 +965,133 @@ module.exports = class Service { } + /** + * + * @function + * @name initIPC + * @description Fonction utilisée pour permettra la communication IPC afin de traiter une requête venant de l'administrateur envoyée via ce protocol + * + */ + + initIPC() { + + LOGGER.info("initIPC..."); + + process.on('message', (request) => { + + LOGGER.debug("Service child received request: "); + LOGGER.debug(request); + + // TODO : vérifier que le message vient bien du parent ? + + let response; + try { + response = this.computeAdminRequest(request); + } catch(error) { + LOGGER.error("Erreur lors de l'exécution de la requête: " + error); + process.send(error); + return false; + } + + try { + response._uuid = request._uuid; + process.send(response); + } catch(error) { + LOGGER.error("Erreur lors de l'exécution de la requête: " + error); + process.send(error); + return false; + } + + return true; + + }); + + } + + /** + * + * @function + * @name computeAdminRequest + * @description Fonction utilisée pour traiter une requête venant de l'administrateur + * @param {object} request - Instance fille de la classe Request + * + */ + + computeAdminRequest(request) { + + LOGGER.info("computeAdminRequest..."); + + // On part du principe qu'un maximum de vérifications ont été faites avant par l'administrateur + // On essaye de réduire l'exécution par le service + + // Le passage potentiel par IPC fait perdre les méthodes donc dans la suite, on est obligé de prendre les attributs avec _ + + // En fonction du type de la requête, on va appeler différentes fonctions + // Le if est un choix modifiable. Pour le moment c'est ainsi car dans le cas du serviceProcess, on ne peut pas y échapper. + if (request._type === "healthRequest") { + return this.computeHealthRequest(request); + } else { + throw errorManager.createError("Unknown request type"); + } + + } + + /** + * + * @function + * @name computeHealthRequest + * @description Fonction utilisée pour connaître l'état du service + * @param {HealthRequest} healthRequest - Instance de la classe HealthRequest + * + */ + + computeHealthRequest(healthRequest) { + + LOGGER.info("computeHealthRequest..."); + + let healthResponse = new HealthResponse(); + let nbRed = 0; + let nbSources = this._sourceManager.loadedSourceId.length; + let serviceState = {}; + serviceState.sources = new Array(); + + // Récupération de la disponibilité de chaque source + for (let sourceId in this._sourceManager.sources) { + + let sourceState = this._sourceManager.sources[sourceId].state; + + if (sourceState === "red") { + nbRed++; + } else if (sourceState === "green" || sourceState === "init") { + // Les cas "green" et "init" sont considérés comme équivalent + } else { + // État inconnu donc on lève une alerte et on le met manuellement à "unknown" pour ne pas renvoyer n'importe quoi à l'administrateur + sourceState = "unknown"; + nbRed++; + } + + serviceState.sources.push({id: sourceId, state: sourceState}); + + } + + // Pour faire simple : + // - un service est orange si une des sources est indisponible + // - service est rouge si la moitié, ou plus, de ses sources sont indisponibles + if (nbRed > 0) { + + if (nbRed >= nbSources / 2) { + serviceState.state = "red"; + } else { + serviceState.state = "orange"; + } + + } else { + serviceState.state = "green"; + } + + healthResponse.serviceStates.push(serviceState); + return healthResponse; + + } + } diff --git a/src/js/service/serviceAdministered.js b/src/js/service/serviceAdministered.js index 2789d8c..1a06880 100644 --- a/src/js/service/serviceAdministered.js +++ b/src/js/service/serviceAdministered.js @@ -65,5 +65,16 @@ module.exports = class ServiceAdministered { } + /** + * + * @function + * @name computeRequest + * @description Fonction pour utiliser pour envoyer une requête à un service selon le mode adaptée à la classe fille. Elle doit être ré-écrite dans chaque classe fille. + * + */ + computeRequest() { + + } + } diff --git a/src/js/service/serviceInsider.js b/src/js/service/serviceInsider.js index 4505850..adc1b33 100644 --- a/src/js/service/serviceInsider.js +++ b/src/js/service/serviceInsider.js @@ -143,4 +143,21 @@ module.exports = class ServiceInsider extends ServiceAdministered { } + /** + * + * @function + * @name computeRequest + * @description Fonction pour utiliser pour envoyer une requête à un service selon le mode adaptée à la classe fille. Elle doit être ré-écrite dans chaque classe fille. + * @param {object} request - Instance fille de la classe Request + * + */ + computeRequest(request) { + + LOGGER.info("computeRequest..."); + + // L'instance de Service est accessible, il suffit de faire appel à la fonction qui traite les requêtes + return this._serviceInstance.computeAdminRequest(request); + + } + } diff --git a/src/js/service/serviceManager.js b/src/js/service/serviceManager.js index 443abad..405249e 100644 --- a/src/js/service/serviceManager.js +++ b/src/js/service/serviceManager.js @@ -4,6 +4,7 @@ const log4js = require('log4js'); const Service = require('./service'); const ServiceInsider = require('./serviceInsider'); const ServiceProcess = require('./serviceProcess'); +const errorManager = require('../utils/errorManager'); // Création du LOGGER const LOGGER = log4js.getLogger("SERVICEMANAGER"); @@ -128,4 +129,51 @@ module.exports = class serviceManager { } + /** + * + * @function + * @name computeRequest + * @description Gestion d'une requête pour un service + * La requête est envoyé au service puis la réponse du service est retournée + * @param {string} serviceId - Id du service selon l'administrateur + * @param {object} request - Instance fille de la classe Request + * + */ + async computeRequest(serviceId, request) { + + LOGGER.info("computeRequest..."); + + // Quelques vérifications + if (!serviceId) { + throw errorManager.createError("Aucun id de service"); + } + if (typeof(serviceId) !== "string") { + throw errorManager.createError("L'id de service n'est pas une string"); + } else { + LOGGER.debug("serviceId: " + serviceId); + } + if (!request) { + throw errorManager.createError("Aucune requête"); + } + if (typeof(request) !== "object") { + throw errorManager.createError("La requête n'est pas un objet"); + } else { + LOGGER.debug("request:"); + LOGGER.debug(request); + } + + // On récupère le service administré + let administeredService = this._loadedServiceAdministeredCatalog[serviceId]; + if (!administeredService) { + LOGGER.error("Aucun service associé à cet ID: " + serviceId); + throw errorManager.createError("Unknown service : " + serviceId); + } + + // On envoit la requête et renvoit la réponse + let response = await administeredService.computeRequest(request); + + return response; + + } + } \ No newline at end of file diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js index ab8c978..a9ef1d0 100644 --- a/src/js/service/serviceProcess.js +++ b/src/js/service/serviceProcess.js @@ -4,6 +4,11 @@ const log4js = require('log4js'); const { fork } = require('child_process'); const ServiceAdministered = require('./serviceAdministered'); +const errorManager = require('../utils/errorManager'); +const healthResponse = require('../responses/healthResponse'); +const { + setInterval, + } = require('node:timers/promises'); // Création du LOGGER const LOGGER = log4js.getLogger("SERVICEPRO"); @@ -39,6 +44,12 @@ module.exports = class ServiceProcess extends ServiceAdministered { // Instance de childProcess quand le processus est lancé this._serviceInstance = {}; + // Compteur des requêtes envoyées effectivement au service + this._requestCount = 0; + + // Liste des réponses en attente de lecture + this._unReadResponses = {}; + } /** @@ -82,12 +93,106 @@ module.exports = class ServiceProcess extends ServiceAdministered { // Création du service via un fork : on lance simplement service/main.js LOGGER.info("Fork du processus pour créer le service..."); - this._serviceInstance = fork("./src/js/service/main.js", [this._configurationLocation], serviceOptions); + this._serviceInstance = fork("./src/js/service/main.js", [this._configurationLocation, "child"], serviceOptions); LOGGER.info("Processus enfant créé"); + // Puisqu'un processus enfant a été créé et qu'il y a un canal IPC entre eux, on instancie la gestion des messages + this._serviceInstance.on("message", (response) => { + + LOGGER.debug("Parent got message:"); + LOGGER.debug(response); + + // On stocke la réponse + this._unReadResponses[response._uuid] = response; + + }); + return true; } + /** + * + * @function + * @name computeRequest + * @description Fonction pour utiliser pour envoyer une requête à un service selon le mode adaptée à la classe fille. Elle doit être ré-écrite dans chaque classe fille. + * @param {object} request - Instance fille de la classe Request + * + */ + async computeRequest(request) { + + LOGGER.info("computeRequest..."); + + // On vérifie que le child est toujours connecté + if (!this._serviceInstance.connected) { + throw errorManager.createError("Impossible d'envoyer une requête au service car non connecté"); + } else { + + LOGGER.debug("Le service child est connecté"); + + // On crée un UUID qui va permettre de faire le lien avec la réponse + // TODO : que se passe-t-il si l'instance tourne longtemps, y'a-t-il une limite ? + this._requestCount++; + request._uuid = this._requestCount.toString(); + LOGGER.debug("Création d'un UUID:" + request._uuid); + + // On envoit la requête au child + if (!this._serviceInstance.send(request)) { + throw errorManager.createError("Erreur lors de l'envoie de la requête"); + } + + // On attend la réponse et on la renvoit + let response = await this.waitResponse(request._uuid); + + return response; + + } + + } + + /** + * + * @function + * @name waitResponse + * @description Fonction pour utiliser pour récupérer la réponse d'une requête une fois qu'elle est arrivée + * @param {string} uuid - UUID de la requête + * + */ + async waitResponse(uuid) { + + LOGGER.info("waitResponse..."); + + let response = {}; + + // Toutes les 10ms on va voir si la réponse est disponible + const interval = 10; + + for await (const startTime of setInterval(interval, Date.now())) { + + // Est-ce que la réponse est disponible ? + if (this._unReadResponses[uuid]) { + LOGGER.debug("Response found for uuid: " + uuid); + response = this._unReadResponses[uuid]; + LOGGER.debug(response); + // On supprime la réponse de l'objet pour libérer la mémoire + delete this._unReadResponses[uuid]; + LOGGER.debug(this._unReadResponses); + break; + } + + // Gestion du timeout : 1min + const now = Date.now(); + if ((now - startTime) > 60000) { + LOGGER.info("Timeout atteint pour l'attente de la réponse"); + response = errorManager.createError("Pas de réponse reçue dans le temps imparti"); + break; + } + + } + + return response; + + } + } diff --git a/src/js/sources/source.js b/src/js/sources/source.js index a2ccbf0..8c34463 100644 --- a/src/js/sources/source.js +++ b/src/js/sources/source.js @@ -42,10 +42,12 @@ module.exports = class Source { // État de la connexion de la source this._connected = false; - // État de la source (même si connectée, elle peut être disfonctionnelle) - // Peut être : "green" si la dernière requête a fonctionnée, "orange" si la source est connectée mais injoignable, "red" à l'initialisation ou si plus gros problème + // État de la source (même si connectée, elle peut être dysfonctionnelle) + // "green" si la dernière requête a fonctionnée, + // "red" si la donnée n'est plus accessible, + // "init" à sa création (utile pour une requête /health de l'administrateur) // Ajouter la gestion de ce paramètre dans chaque classe fille - this._state = "red"; + this._state = "init"; } diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index de76b91..fe30acc 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -30,7 +30,7 @@ module.exports = class sourceManager { this._checkedSourceId = new Array(); // Catalogue des sources du manager - this._source = {}; + this._sources = {}; // Descriptions des sources chargées par le manager this._loadedSourceConfiguration = {}; @@ -59,12 +59,23 @@ module.exports = class sourceManager { /** * * @function - * @name get source - * @description Récupérer l'ensemble des ids de sources chargées + * @name get loadedSourceId + * @description Récupérer l'ensemble des id de sources chargées * */ - get source() { - return this._source; + get loadedSourceId() { + return this._loadedSourceId; + } + + /** + * + * @function + * @name get sources + * @description Récupérer l'ensemble des sources chargées + * + */ + get sources() { + return this._sources; } /** @@ -105,7 +116,7 @@ module.exports = class sourceManager { getSourceById(id) { if (this.isLoadedSourceAvailable(id)) { - return this._source[id]; + return this._sources[id]; } else { return null; } @@ -915,7 +926,7 @@ module.exports = class sourceManager { let source; - if (this._source[sourceJsonObject.id]) { + if (this._sources[sourceJsonObject.id]) { LOGGER.info("La source " + sourceJsonObject.id + " existe déjà"); return true; } @@ -948,7 +959,7 @@ module.exports = class sourceManager { // Notamment dans la gestion (ajout/suppression/modification) de sources durant la vie du service this._loadedSourceId.push(sourceJsonObject.id); this._loadedSourceConfiguration[sourceJsonObject.id] = sourceJsonObject; - this._source[sourceJsonObject.id] = source; + this._sources[sourceJsonObject.id] = source; return true; @@ -969,7 +980,7 @@ module.exports = class sourceManager { try { - await this._source[sourceId].connect(); + await this._sources[sourceId].connect(); LOGGER.info("Source connectee."); return true; diff --git a/test/functional/request/cucumber/features/support/world.js b/test/functional/request/cucumber/features/support/world.js index 0f3002c..7c7c281 100644 --- a/test/functional/request/cucumber/features/support/world.js +++ b/test/functional/request/cucumber/features/support/world.js @@ -739,6 +739,7 @@ class road2World { } else { // On part du principe que c'est du GeoJSON + // TODO : ajouter la gestion du polyline if (curIso.coordinates) { refIso = turf.polygon(refIso.coordinates); diff --git a/test/integration/mocha/requests/integrationHealthRequest.js b/test/integration/mocha/requests/integrationHealthRequest.js new file mode 100644 index 0000000..c29b468 --- /dev/null +++ b/test/integration/mocha/requests/integrationHealthRequest.js @@ -0,0 +1,31 @@ +const assert = require('assert'); +const HealthRequest = require('../../../../src/js/requests/healthRequest'); +const logManager = require('../logManager'); + +describe('Test de la classe HealthRequest', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let request = new HealthRequest(); + + describe('Test du constructeur et des getters/setters', function() { + + it('Get type', function() { + assert.equal(request.type, "healthRequest"); + }); + + it('Get verbose', function() { + assert.equal(request.verbose, false); + }); + + it('Set verbose', function() { + request.verbose = true; + assert.equal(request.verbose, true); + }); + + }); + +}); diff --git a/test/integration/mocha/responses/integrationHealthResponse.js b/test/integration/mocha/responses/integrationHealthResponse.js new file mode 100644 index 0000000..31796ce --- /dev/null +++ b/test/integration/mocha/responses/integrationHealthResponse.js @@ -0,0 +1,49 @@ +const assert = require('assert'); +const HealthResponse = require('../../../../src/js/requests/healthResponse'); +const logManager = require('../logManager'); + +describe('Test de la classe HealthResponse', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let response = new HealthResponse(); + + describe('Test du constructeur et des getters/setters', function() { + + it('Get type', function() { + assert.equal(response.type, "healthResponse"); + }); + + it('Get globalState', function() { + assert.equal(response.globalState, "unknown"); + }); + + it('Set globalState', function() { + response.globalState = "green"; + assert.equal(response.globalState, "green"); + }); + + it('Get adminState', function() { + assert.equal(response.adminState, "unknown"); + }); + + it('Set adminState', function() { + response.adminState = "green"; + assert.equal(response.adminState, "green"); + }); + + it('Get serviceStates', function() { + assert.deepEqual(response.serviceStates, new Array()); + }); + + it('Set serviceStates', function() { + response.serviceStates.push = {"serviceId":"unknown","state":"green"}; + assert.deepEqual(response.serviceStates[0], {"serviceId":"unknown","state":"green"}); + }); + + }); + +}); diff --git a/test/unit/mocha/requests/testsRequest.js b/test/unit/mocha/requests/testsRequest.js index 60a7f48..f4553bc 100644 --- a/test/unit/mocha/requests/testsRequest.js +++ b/test/unit/mocha/requests/testsRequest.js @@ -9,7 +9,7 @@ describe('Test de la classe Request', function() { logManager.manageLogs(); }); - let request = new Request("route", "corse-osm", "routeRequest"); + let request = new Request("route", "routeRequest"); describe('Test du constructeur et des getters', function() { @@ -17,10 +17,6 @@ describe('Test de la classe Request', function() { assert.equal(request.operation, "route"); }); - it('Get Resource', function() { - assert.equal(request.resource, "corse-osm"); - }); - it('Get Type', function() { assert.equal(request.type, "routeRequest"); }); @@ -34,11 +30,6 @@ describe('Test de la classe Request', function() { assert.equal(request.operation, "nearest"); }); - it('Set Resource', function() { - request.resource = "corse-osm-2"; - assert.equal(request.resource, "corse-osm-2"); - }); - // Le type ne devrait pas changer car il dépend de la classe fille appelée it('Set Type ne change rien', function() { request.type = "otherRequest"; diff --git a/test/unit/mocha/responses/testsResponse.js b/test/unit/mocha/responses/testsResponse.js index 1c7d0ea..4fe3f10 100644 --- a/test/unit/mocha/responses/testsResponse.js +++ b/test/unit/mocha/responses/testsResponse.js @@ -11,15 +11,15 @@ describe('Test de la classe Response', function() { describe('Test du constructeur et des getters/setters', function() { - let response = new Response("mon-id"); + let response = new Response("mon-type"); - it('Get Resource', function() { - assert.equal(response.resource, "mon-id"); + it('Get Type', function() { + assert.equal(response.type, "mon-type"); }); - it('Set Resource', function() { - response.resource = "nouvel-id"; - assert.equal(response.resource, "nouvel-id"); + it('Set Type n\'existe pas car non modifiable', function() { + response.type = "nouveau-type"; + assert.equal(response.type, "mon-type"); }); }); From 514804a4e2a06f351e3ad00f08d669b3c75e3220 Mon Sep 17 00:00:00 2001 From: lgrd Date: Fri, 10 Mar 2023 10:04:53 +0100 Subject: [PATCH 66/93] [doc] add a json schema for constraints (#44) * [doc] add a json schema for constraints * [fix] add a little enum --- .../resources/constraint.schema.json | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 documentation/configuration/resources/constraint.schema.json diff --git a/documentation/configuration/resources/constraint.schema.json b/documentation/configuration/resources/constraint.schema.json new file mode 100644 index 0000000..72d80bd --- /dev/null +++ b/documentation/configuration/resources/constraint.schema.json @@ -0,0 +1,190 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema#", + "title": "Generated schema for Root", + "type": "object", + "properties": { + "id": { + "type": "string", + "enum": ["constraints"] + }, + "defaultPreferredCostRatio": { + "type": "number" + }, + "defaultAvoidCostRatio": { + "type": "number" + }, + "values": { + "type": "array", + "items": { + "anyOf" : [ + {"type": "object", + "properties": { + "keyType": { + "type": "string", + "enum": ["name-osrm"] + }, + "key": { + "type": "string" + }, + "availableConstraintType": { + "type": "array", + "items": { + "type": "string" + } + }, + "availableValues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "field": { + "type": "string" + } + }, + "required": [ + "value", + "field" + ] + } + } + }, + "required": [ + "keyType", + "key", + "availableConstraintType", + "availableValues" + ] + }, + {"type": "object", + "properties": { + "keyType": { + "type": "string", + "enum": ["name-pgr"] + }, + "key": { + "type": "string" + }, + "availableConstraintType": { + "type": "array", + "items": { + "type": "string" + } + }, + "availableValues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "field": { + "type": "string" + }, + "condition": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "type", + "value" + ] + } + }, + "required": [ + "value", + "field", + "condition" + ] + } + } + }, + "required": [ + "keyType", + "key", + "availableConstraintType", + "availableValues" + ]}, + {"type": "object", + "properties": { + "keyType": { + "type": "string", + "enum": ["numerical-pgr"] + }, + "key": { + "type": "string" + }, + "availableConstraintType": { + "type": "array", + "items": { + "type": "string" + } + }, + "field": { + "type": "string" + } + }, + "required": [ + "keyType", + "key", + "availableConstraintType", + "field" + ]}, + {"type": "object", + "properties": { + "keyType": { + "type": "string", + "enum": ["name-valhalla"] + }, + "key": { + "type": "string" + }, + "availableConstraintType": { + "type": "array", + "items": { + "type": "string" + } + }, + "availableValues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "field": { + "type": "string" + } + }, + "required": [ + "value", + "field" + ] + } + } + }, + "required": [ + "keyType", + "key", + "availableConstraintType", + "availableValues" + ]} + ] + }, + "required": [ + "id", + "values" + ] +} +} +} From c3eb197f4ecc8cea6e8e6cdf3f7c93b68cbeb8c0 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Fri, 10 Mar 2023 16:49:12 +0100 Subject: [PATCH 67/93] feat(test): add cucumber test for new GET admin/1.0.0/services --- .../cucumber/configurations/local-admin.json | 3 ++- .../cucumber/features/req-admin-1.0.0.feature | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/test/functional/request/cucumber/configurations/local-admin.json b/test/functional/request/cucumber/configurations/local-admin.json index ed251b6..c65a56a 100644 --- a/test/functional/request/cucumber/configurations/local-admin.json +++ b/test/functional/request/cucumber/configurations/local-admin.json @@ -6,7 +6,8 @@ "admin": { "1.0.0": { "health": "/admin/1.0.0/health", - "version": "/admin/1.0.0/version" + "version": "/admin/1.0.0/version", + "services": "/admin/1.0.0/services" } } }, diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index 209c80d..6766bbd 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -64,4 +64,21 @@ Feature: Road2 with data | test | other | When I send the request Then the server should send a response with status 404 - And the response should contain "Not found" \ No newline at end of file + And the response should contain "Not found" + + Scenario: [admin/1.0.0] Configurations des services + Given an "GET" request on operation "services" in api "admin" "1.0.0" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain "application" + And the response should contain an attribute "[0].id" + + Scenario: [admin/1.0.0] Configurations des services en POST ne marche pas + Given an "POST" request on operation "services" in api "admin" "1.0.0" + And with query parameters: + | key | value | + | test | other | + When I send the request + Then the server should send a response with status 404 + And the response should contain "Not found" From f560b91029c2dba3724e9ffba991df73fe5913e1 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Fri, 10 Mar 2023 16:53:13 +0100 Subject: [PATCH 68/93] feat(admin): add GET admin/1.0.0/services route --- changelog.md | 3 ++ .../apis/administration/1.0.0/api.yaml | 7 ---- src/js/administrator/administrator.js | 36 +++++++++++++++++++ src/js/apis/admin/1.0.0/index.js | 28 +++++++++++++++ 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 0ad95e8..5d1fcbd 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,9 @@ ## 2.1.0 +ADDED: + - Ajout de la route GET /admin/1.0.0/services dans l'API d'administration + CHANGED: - La documentation de l'API d'administration a été grandement enrichie. - La route /health a une réponse plus complète et est vraiment codée pour prendre en compte l'état de chaque service et chaque source disponibles. diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index a0ef73e..a3be6de 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -186,13 +186,6 @@ paths: Cette requête retourne l'ensemble des services connus par l'administrateur. Cette liste peut être vide si aucun service n'a été configuré. operationId: "services" - parameters: - - name: "withId" - description: "Si on souhaite récupérer les ids des services. Ces ids sont normalement dans la configuration de l'administrateur. " - in: "query" - required: false - schema: - type: "boolean" responses: 200: description: "successful operation" diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 5a79ed0..593715d 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -583,6 +583,42 @@ module.exports = class Administrator { } + + /** + * + * @function + * @name getServicesConfigurations + * @description Récupération de la configuration des services + * @param {json} response - Reponse json contenant les configuration de services + * + */ + + getServicesConfigurations(parameters) { + + LOGGER.info("getServicesConfigurations..."); + + let responses = new Array(); + + // Pour chaque service, on récupère la configuration depuis le fichier de configuration + for (let i = 0; i < this._configuration.administration.services.length; i++) { + + let curServiceId = this._configuration.administration.services[i].id; + + LOGGER.debug("Lecture fichier de configuration du service : " + curServiceId); + let curServiceConfFile = this._configuration.administration.services[i].configuration + let configuration = JSON.parse(fs.readFileSync(curServiceConfFile)); + + // Ajout de l'id + configuration.id = curServiceId + + responses.push(configuration) + } + + return responses; + } + } + + diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index 9cc5829..30c5d79 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -68,6 +68,34 @@ router.route("/health") }); +// Services +// Pour avoir des informations sur les services +router.route("/services") + + .get(async function(req, res, next) { + + LOGGER.debug("requete GET sur /admin/1.0.0/services?"); + LOGGER.debug(req.originalUrl); + + // On récupère l'instance d'Administrator pour répondre aux requêtes + let administrator = req.app.get("administrator"); + + // on récupère l'ensemble des paramètres de la requête + let parameters = req.query; + LOGGER.debug(parameters); + + try { + + const servicesResponse = administrator.getServicesConfigurations(parameters) + res.set('content-type', 'application/json'); + res.status(200).json(servicesResponse); + + } catch (error) { + return next(error); + } + + }); + // Gestion des erreurs // Cette partie doit être placée après la définition des routes normales // --- From d0b784c3d073e0a55f1137930468b1b5575a0bed Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Mon, 13 Mar 2023 10:12:49 +0100 Subject: [PATCH 69/93] feat(admin): add try/catch for service config file read --- .../apis/administration/1.0.0/api.yaml | 6 +++++ src/js/administrator/administrator.js | 23 ++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index a3be6de..d8cfbab 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -201,6 +201,12 @@ paths: id: type: "string" - $ref: "#/components/schemas/serviceConfiguration" + 500: + description: "Internal error" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" post: tags: - "Gestion des services" diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 593715d..d062e17 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -10,6 +10,7 @@ const serverManager = require('../server/serverManager'); const serviceManager = require('../service/serviceManager'); const apisManager = require('../apis/apisManager'); const HealthResponse = require('../responses/healthResponse'); +const errorManager = require('../utils/errorManager'); // Création du LOGGER const LOGGER = log4js.getLogger("ADMINISTRATOR"); @@ -602,16 +603,22 @@ module.exports = class Administrator { // Pour chaque service, on récupère la configuration depuis le fichier de configuration for (let i = 0; i < this._configuration.administration.services.length; i++) { - let curServiceId = this._configuration.administration.services[i].id; + const curServiceId = this._configuration.administration.services[i].id; LOGGER.debug("Lecture fichier de configuration du service : " + curServiceId); - let curServiceConfFile = this._configuration.administration.services[i].configuration - let configuration = JSON.parse(fs.readFileSync(curServiceConfFile)); - - // Ajout de l'id - configuration.id = curServiceId - - responses.push(configuration) + try { + const curServiceConf = this._configuration.administration.services[i].configuration + const configurationLocation = path.resolve(path.dirname(this._configurationPath), curServiceConf); + let configuration = JSON.parse(fs.readFileSync(configurationLocation)); + + // Ajout de l'id + configuration.id = curServiceId + + responses.push(configuration) + + } catch (error) { + throw errorManager.createError("Can't read service configuration file : " + error) + } } return responses; From c3fa0700aaabad77253481d87cf2168185a54692 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 13 Mar 2023 11:13:24 +0100 Subject: [PATCH 70/93] [doc] detele attribute inside error response --- documentation/apis/administration/1.0.0/api.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index d8cfbab..6f7160a 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -746,8 +746,6 @@ components: error: type: "object" properties: - errorType: - type: "string" message: type: "string" From 6afd5a95c88eaa527a99c65a65cbac458391f913 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Mon, 13 Mar 2023 12:15:53 +0100 Subject: [PATCH 71/93] feat(init): no stop of service if source or resource dir is empty --- src/js/resources/resourceManager.js | 2 +- src/js/sources/sourceManager.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index ac13fe5..80e9221 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -88,7 +88,7 @@ module.exports = class resourceManager { if (fileList.length === 0) { LOGGER.warn("Le dossier " + directory + " est vide"); - return false; + return true; } for (let i = 0; i < fileList.length; i++) { diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index fe30acc..b3b226a 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -176,7 +176,7 @@ module.exports = class sourceManager { if (fileList.length === 0) { LOGGER.warn("Le dossier " + directory + " est vide"); - return false; + return true; } for (let i = 0; i < fileList.length; i++) { @@ -1005,8 +1005,8 @@ module.exports = class sourceManager { LOGGER.info("Connexion de l'ensemble des sources..."); if (this._loadedSourceId.length === 0) { - LOGGER.error("Aucune source n'est disponible"); - return false; + LOGGER.warn("Aucune source n'est disponible"); + return true; } try { From e6b9331670d058bff66f59fb23dc01bae74c9986 Mon Sep 17 00:00:00 2001 From: lgrd Date: Mon, 13 Mar 2023 14:09:07 +0100 Subject: [PATCH 72/93] [del] onStart option and feature (#48) * [del] onStart option and feature * [fix] changelog updated correctly --- changelog.md | 3 ++ docker/config/road2.json | 1 - .../apis/administration/1.0.0/api.yaml | 3 -- .../administration/admin_model.yaml | 4 --- documentation/developers/concepts.md | 2 +- src/js/administrator/administrator.js | 35 +++---------------- src/js/road2.js | 8 ++--- test/integration/mocha/config/road2.json | 1 - 8 files changed, 13 insertions(+), 44 deletions(-) diff --git a/changelog.md b/changelog.md index 5d1fcbd..012d5d9 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,9 @@ CHANGED: - La documentation de l'API d'administration a été grandement enrichie. - La route /health a une réponse plus complète et est vraiment codée pour prendre en compte l'état de chaque service et chaque source disponibles. +DELETED: + - option onStart inside admin configuration is deleted + ## 2.0.0 ADDED: diff --git a/docker/config/road2.json b/docker/config/road2.json index 4799842..fc870d8 100644 --- a/docker/config/road2.json +++ b/docker/config/road2.json @@ -8,7 +8,6 @@ { "id": "main", "configuration": "/home/docker/config/service.json", - "onStart": "true", "creationType": "newProcess" } ], diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index 6f7160a..e3294fb 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -776,9 +776,6 @@ components: configuration: type: "string" example: "/home/docker/config/service.json" - onStart: - type: "string" - example: "true" creationType: type: "string" example: "newProcess" diff --git a/documentation/configuration/administration/admin_model.yaml b/documentation/configuration/administration/admin_model.yaml index db8b60e..8408be8 100644 --- a/documentation/configuration/administration/admin_model.yaml +++ b/documentation/configuration/administration/admin_model.yaml @@ -34,10 +34,6 @@ "configuration": type: string required: true - # Indiquer si on souhaite que le service soit démarré au lancement de l'admin - "onStart": - type: string - required: true # Type de création du service. Pour le moment, il seul 'newProcess' est accepté. On crée un nouveau processus pour le service. "creationType": type: string diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index f1db7d5..49deb14 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -128,7 +128,7 @@ Le premier point d'entrée possible est le fichier `src/js/road2.js`. Ce fichier Cet administrateur permet plusieurs choses : - On peut le lancer uniquement pour vérifier la bonne configuration de l'administrateur et des services associés. Dans ce cas là, le processus s'éteint après la vérification et renvoie un code d'erreur permettant de déterminer s'il y a eu un problème et son type. -- On peut le lancer en mode serveur pour administrer un ou plusieurs services via une API HTTP(S). Dans ce cas là, il est possible de lui demander de lancer les service à son démarrage. Il sera aussi possible de les démarrer plus tard. +- On peut le lancer en mode serveur pour administrer un ou plusieurs services via une API HTTP(S). Dans ce cas là, l'administrateur va lancer tous les services déjà configurés. Il sera aussi possible d'en créer d'autres plus tard. Un administrateur a été créé pour réaliser des tâches qui auraient gêné la bonne exécution du service. diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index d062e17..a758188 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -204,22 +204,6 @@ module.exports = class Administrator { - } - - if (!curServiceConf.onStart) { - LOGGER.error("Mauvaise configuration: 'onStart' absent."); - return false; - } else { - - LOGGER.debug("configuration.onStart présent"); - - if (curServiceConf.onStart !== "true" && curServiceConf.onStart !== "false") { - LOGGER.error("Mauvaise configuration: 'onStart' doit être 'true' ou 'false'."); - return false; - } else { - LOGGER.debug("configuration.onStart bien configuré"); - } - } if (!curServiceConf.creationType) { @@ -423,31 +407,22 @@ module.exports = class Administrator { /** * * @function - * @name createServicesOnStart - * @description Création des services gérés par cet administrateur dont la création a été demandé au démarrage de l'administrateur + * @name createServices + * @description Création des services gérés par cet administrateur * */ - async createServicesOnStart() { + async createServices() { - LOGGER.info("Vérification et création des services onStart=true..."); + LOGGER.info("Vérification et création des services"); - // Pour chaque service, on va voir s'il doit être démarré - // Si oui, on vérifie sa configuration puis on le démarre + // Pour chaque service, on vérifie sa configuration puis on le démarre for (let i = 0; i < this._configuration.administration.services.length; i++) { let curIASConf = this._configuration.administration.services[i]; LOGGER.debug("Analyse du service : " + curIASConf.id); - if (curIASConf.onStart === "false") { - // il n'y a rien à faire - LOGGER.debug("Le service " + curIASConf.id + " n'est à démarrer tout de suite"); - continue; - } else { - LOGGER.info("Le service " + curIASConf.id + " est à démarrer"); - } - // Récupération de la configuration LOGGER.info("Récupération de la configuration du service"); let serviceConfLocation = path.resolve(path.dirname(this._configurationPath), curIASConf.configuration); diff --git a/src/js/road2.js b/src/js/road2.js index 56a2f48..6cdf6ed 100644 --- a/src/js/road2.js +++ b/src/js/road2.js @@ -62,13 +62,13 @@ async function start() { LOGGER.debug("Serveur administrateur créé"); // Création des services au démarrage si demandé - LOGGER.info("Création des services demandés au démarrage..."); + LOGGER.info("Création des services..."); - if (!(await administrator.createServicesOnStart())) { - LOGGER.error("Problèmes lors du démarrage des services demandés. Reconfigurez les et relancez leur démarrage."); + if (!(await administrator.createServices())) { + LOGGER.error("Problèmes lors du démarrage des services. Reconfigurez les et relancez leur démarrage."); // On n'éteint pas le serveur d'administration car les services pourront être reconfiguré et démarrés par l'API } else { - LOGGER.info("Les services concernés ont été démarré"); + LOGGER.info("Les services ont été démarré"); } } diff --git a/test/integration/mocha/config/road2.json b/test/integration/mocha/config/road2.json index 147ad96..7568db7 100644 --- a/test/integration/mocha/config/road2.json +++ b/test/integration/mocha/config/road2.json @@ -8,7 +8,6 @@ { "id": "main", "configuration": "./service.json", - "onStart": "true", "creationType": "newProcess" } ], From 69f421f7d466b769d651e3b9dfd55eb4e90a283b Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Mon, 13 Mar 2023 15:10:06 +0100 Subject: [PATCH 73/93] feat(test): add test for log in case of empty source or resource dir --- src/js/resources/resourceManager.js | 2 +- src/js/sources/sourceManager.js | 2 +- .../cucumber/features/conf-service.feature | 16 ++++++++++++++++ .../cucumber/features/support/steps.js | 4 ++++ .../cucumber/features/support/world.js | 10 ++++++++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/js/resources/resourceManager.js b/src/js/resources/resourceManager.js index 80e9221..e07c2f9 100644 --- a/src/js/resources/resourceManager.js +++ b/src/js/resources/resourceManager.js @@ -87,7 +87,7 @@ module.exports = class resourceManager { } if (fileList.length === 0) { - LOGGER.warn("Le dossier " + directory + " est vide"); + LOGGER.warn("Le dossier des resources est vide '" + directory + "'"); return true; } diff --git a/src/js/sources/sourceManager.js b/src/js/sources/sourceManager.js index b3b226a..8b23b6f 100644 --- a/src/js/sources/sourceManager.js +++ b/src/js/sources/sourceManager.js @@ -175,7 +175,7 @@ module.exports = class sourceManager { } if (fileList.length === 0) { - LOGGER.warn("Le dossier " + directory + " est vide"); + LOGGER.warn("Le dossier des sources est vide '" + directory + "'"); return true; } diff --git a/test/functional/configuration/cucumber/features/conf-service.feature b/test/functional/configuration/cucumber/features/conf-service.feature index 82991b1..15db95d 100644 --- a/test/functional/configuration/cucumber/features/conf-service.feature +++ b/test/functional/configuration/cucumber/features/conf-service.feature @@ -396,6 +396,14 @@ Feature: Road2 service configuration Then the configuration analysis should give an exit code 1 Then the server log should contain "Mauvaise configuration: Champ 'application:resources:directories' manquant !" + Scenario: [service.json] (resources.directories sans aucune resource) + Given a valid configuration + And an empty directory "empty_resource" + And with parameter "empty_resource" for attribute "application.resources.directories.[0]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le dossier des resources est vide" + # TODO # Tester un dossier de ressources dont une des ressources ne peut être lues @@ -467,6 +475,14 @@ Feature: Road2 service configuration Then the configuration analysis should give an exit code 1 Then the server log should contain "Mauvaise configuration: Champ 'application:sources:directories' manquant !" + Scenario: [service.json] (sources.directories sans aucune sources) + Given a valid configuration + And an empty directory "empty_sources" + And with parameter "empty_sources" for attribute "application.sources.directories.[0]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le dossier des sources est vide" + # TODO # Tester un dossier de ressources dont une des ressources ne peut être lues diff --git a/test/functional/configuration/cucumber/features/support/steps.js b/test/functional/configuration/cucumber/features/support/steps.js index ab6a7f8..9c9ba65 100644 --- a/test/functional/configuration/cucumber/features/support/steps.js +++ b/test/functional/configuration/cucumber/features/support/steps.js @@ -50,6 +50,10 @@ Given("a file {string} non readable", function(relativeFilePath) { this.createFile(relativeFilePath, "", false); }); +Given("an empty directory {string}", function(dirname) { + this.createDir(dirname); +}); + Given("a server configuration non readable", function() { this.nonReadableServerConfiguration(); }); diff --git a/test/functional/configuration/cucumber/features/support/world.js b/test/functional/configuration/cucumber/features/support/world.js index 9fc42bd..d3d7dab 100644 --- a/test/functional/configuration/cucumber/features/support/world.js +++ b/test/functional/configuration/cucumber/features/support/world.js @@ -491,6 +491,16 @@ class road2World { } + // Creation de répertoire + createDir(dirname) { + try { + fs.mkdirSync(path.join(this._tmpDirConf, dirname)); + } catch(error) { + throw "Can't create directory " + dirname + " : " + error; + } + return true + } + createWrongJSONFile(relativeFilePath) { try { From ba13b695ca09280046360861648bbb87379c5740 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 13 Mar 2023 15:28:06 +0100 Subject: [PATCH 74/93] [doc] update concepts doc --- documentation/developers/concepts.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index f1db7d5..42da64d 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -144,9 +144,11 @@ Ce service est l'objet qui permet de gérer les ressources proposées par l'inst Chaque ressource contient plusieurs sources. Étant donné que plusieurs ressources peuvent pointer vers des sources communes, le service contient un catalogue de sources uniques et un manager de ces sources. -Lorsque l'application est lancée, on commence par lire la configuration de l'application pour être capable d'instancier le logger. Une fois que le logger est chargé, on vérifie complètement la configuration. +Lorsque l'application est lancée, on commence par lire la configuration de l'application pour être capable d'instancier le logger. -Après cela, on charge les ressources et les sources du service indiquées dans la configuration. C'est à ce moment que les fichiers sont lus, stockés en RAM si nécessaire, et que les connexions aux bases de données sont effectuées. +Une fois que le logger est chargé, on vérifie complètement la configuration. Il est possible de configurer un service avec des dossiers de sources et de ressources vides. Ils pourront être remplis plus tard. Cependant, ces dossier doivent être indiqués lors de la configuration du service. + +Après cela, on charge les ressources et les sources du service indiquées dans la configuration s'il y en a. C'est à ce moment que les fichiers sont lus, stockés en RAM si nécessaire, et que les connexions aux bases de données sont effectuées. Enfin, on finit par charger les APIs exposées par le service. C'est là qu'ExpressJS crée le ou les serveurs Node et charge les routes disponibles. From e23189534508eef95c41fed3e4c61ae8b8dbb217 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Mon, 13 Mar 2023 15:51:58 +0100 Subject: [PATCH 75/93] feat(changelog): update for empty source and resource dir at init --- changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 5d1fcbd..b3c87a1 100644 --- a/changelog.md +++ b/changelog.md @@ -7,7 +7,8 @@ ADDED: CHANGED: - La documentation de l'API d'administration a été grandement enrichie. - - La route /health a une réponse plus complète et est vraiment codée pour prendre en compte l'état de chaque service et chaque source disponibles. + - La route /health a une réponse plus complète et est vraiment codée pour prendre en compte l'état de chaque service et chaque source disponibles. + - Les dossiers de sources et de resources des services peuvent maintenant être vide à l'initialisation. ## 2.0.0 From a45d7a5924f93225cdbb99df455b5247fbdca204 Mon Sep 17 00:00:00 2001 From: Loic Date: Tue, 14 Mar 2023 10:29:10 +0100 Subject: [PATCH 76/93] [test] fix exit code function, add tests --- .../cucumber/features/conf-service.feature | 16 +++++++++++++--- .../cucumber/features/support/steps.js | 2 +- .../cucumber/features/support/world.js | 10 +++------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/test/functional/configuration/cucumber/features/conf-service.feature b/test/functional/configuration/cucumber/features/conf-service.feature index 15db95d..e2665eb 100644 --- a/test/functional/configuration/cucumber/features/conf-service.feature +++ b/test/functional/configuration/cucumber/features/conf-service.feature @@ -363,7 +363,7 @@ Feature: Road2 service configuration Given a valid configuration And with parameter "test" for attribute "application.resources.directories.[1]" in service configuration When I test the configuration - Then the configuration analysis should give an exit code 0 + Then the configuration analysis should give an exit code 1 Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas:" Scenario: [service.json] (resources.directories sur deux dossiers qui n'existent pas et en chemins relatifs) @@ -442,7 +442,7 @@ Feature: Road2 service configuration Given a valid configuration And with parameter "test" for attribute "application.sources.directories.[1]" in service configuration When I test the configuration - Then the configuration analysis should give an exit code 0 + Then the configuration analysis should give an exit code 1 Then the server log should contain "Mauvaise configuration: Le dossier n'existe pas:" Scenario: [service.json] (sources.directories sur deux dossiers qui n'existent pas et en chemins relatifs) @@ -480,8 +480,18 @@ Feature: Road2 service configuration And an empty directory "empty_sources" And with parameter "empty_sources" for attribute "application.sources.directories.[0]" in service configuration When I test the configuration - Then the configuration analysis should give an exit code 0 + Then the configuration analysis should give an exit code 1 Then the server log should contain "Le dossier des sources est vide" + Then the server log should contain "La ressource contient une source non disponible" + + Given a valid configuration + And an empty directory "empty_resource" + And with parameter "empty_resource" for attribute "application.resources.directories.[0]" in service configuration + And an empty directory "empty_sources" + And with parameter "empty_sources" for attribute "application.sources.directories.[0]" in service configuration + When I test the configuration + Then the configuration analysis should give an exit code 0 + Then the server log should contain "Le dossier des resources est vide" # TODO # Tester un dossier de ressources dont une des ressources ne peut être lues diff --git a/test/functional/configuration/cucumber/features/support/steps.js b/test/functional/configuration/cucumber/features/support/steps.js index 9c9ba65..b365f04 100644 --- a/test/functional/configuration/cucumber/features/support/steps.js +++ b/test/functional/configuration/cucumber/features/support/steps.js @@ -133,7 +133,7 @@ When("I test the configuration", function(done) { }); Then("the configuration analysis should give an exit code {int}", function(code) { - assert.equal(this.verifyCommandExitCode(code), true); + assert.equal(this.returnCommandExitCode(), code); }); Then("the server log should contain {string}", function(message) { diff --git a/test/functional/configuration/cucumber/features/support/world.js b/test/functional/configuration/cucumber/features/support/world.js index d3d7dab..de29cb9 100644 --- a/test/functional/configuration/cucumber/features/support/world.js +++ b/test/functional/configuration/cucumber/features/support/world.js @@ -641,15 +641,11 @@ class road2World { } - // Analyse du code de retour de la commande - verifyCommandExitCode(code) { + // Retourne le code de la commande + returnCommandExitCode() { - if (code === this._code) { - return true; - } else { return this._code; - } - + } // Analyse des logs From 0d3602236c9cb5e7f942382b5eb902af5cc6b5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Blanc?= Date: Wed, 15 Mar 2023 14:26:29 +0100 Subject: [PATCH 77/93] feature(admin): add route to get service configuration (#50) * feature(admin): add route to get service configuration * feat(admin): add explicit error status on getting service configuration * feat(admin): only add service id in response when getting all of services * fix(admin): fix error message to get right service from request parameters * feat(test): add cucumber test for /services/:service route * feat(admin): update get service configuration according review * fix(admin): function getServicesConfigurations do not need request parameters * creation and use of serviceRequest, and form changes --------- Co-authored-by: Loic --- documentation/test/integration/readme.md | 1 + src/js/administrator/administrator.js | 99 +++++++++++++++---- .../apis/admin/1.0.0/controller/controller.js | 34 ++++++- src/js/apis/admin/1.0.0/index.js | 39 +++++++- src/js/requests/serviceRequest.js | 56 +++++++++++ .../cucumber/configurations/local-admin.json | 3 +- .../cucumber/features/req-admin-1.0.0.feature | 19 ++++ .../cucumber/features/support/steps.js | 4 + .../cucumber/features/support/world.js | 10 ++ .../requests/integrationServiceRequest.js | 31 ++++++ .../responses/integrationHealthResponse.js | 4 +- 11 files changed, 273 insertions(+), 27 deletions(-) create mode 100644 src/js/requests/serviceRequest.js create mode 100644 test/integration/mocha/requests/integrationServiceRequest.js diff --git a/documentation/test/integration/readme.md b/documentation/test/integration/readme.md index 53f2372..d528721 100644 --- a/documentation/test/integration/readme.md +++ b/documentation/test/integration/readme.md @@ -22,6 +22,7 @@ C'est l'approche bottom-up qui a été choisie pour ces tests. On va tester les - serverManager (server, ExpressJS, log4js, fs, assert) - healthRequest (request) - healthResponse (response) + - serviceRequest (request) - Deuxième niveau: - routeRequest (request, point) diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index a758188..7f41136 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -565,38 +565,99 @@ module.exports = class Administrator { * @function * @name getServicesConfigurations * @description Récupération de la configuration des services - * @param {json} response - Reponse json contenant les configuration de services + * @return {object} responses - Json contenant les configuration de services gérés par l'administrateur * */ - getServicesConfigurations(parameters) { + getServicesConfigurations() { LOGGER.info("getServicesConfigurations..."); let responses = new Array(); + LOGGER.debug("Récupération des configurations de chaque service"); + // Pour chaque service, on récupère la configuration depuis le fichier de configuration - for (let i = 0; i < this._configuration.administration.services.length; i++) { + for (let i = 0; i < this._configuration.administration.services.length; i++) { - const curServiceId = this._configuration.administration.services[i].id; + let curServiceAdminConf = this._configuration.administration.services[i]; - LOGGER.debug("Lecture fichier de configuration du service : " + curServiceId); - try { - const curServiceConf = this._configuration.administration.services[i].configuration - const configurationLocation = path.resolve(path.dirname(this._configurationPath), curServiceConf); - let configuration = JSON.parse(fs.readFileSync(configurationLocation)); - - // Ajout de l'id - configuration.id = curServiceId - - responses.push(configuration) - - } catch (error) { - throw errorManager.createError("Can't read service configuration file : " + error) - } - } + LOGGER.debug("Récupération de la configuration du service : " + curServiceAdminConf.id); + + let configuration = this.readServiceConfiguration(curServiceAdminConf); + + // Ajout de l'id + configuration.id = curServiceAdminConf.id + + responses.push(configuration); + + } + + LOGGER.debug(responses); return responses; + + } + + /** + * + * @function + * @name getServiceConfiguration + * @description Récupération de la configuration d'un service + * @param {string} serviceId - Identifiant du service dont on veut la configuration + * @return {object} response - Contenu du service.json + * + */ + + getServiceConfiguration(serviceId) { + + LOGGER.info("getServiceConfiguration..."); + + // On récupère la configuration admin de ce service, s'il existe + const serviceAdminConf = this._configuration.administration.services.find(service => service.id == serviceId); + + if (serviceAdminConf) { + + LOGGER.debug("Le service " + serviceId + " a été trouvé"); + LOGGER.debug(serviceAdminConf); + return this.readServiceConfiguration(serviceAdminConf); + + } else { + throw errorManager.createError(`Can't find service ${serviceId}`, 404) + } + + } + + /** + * + * @function + * @name readServiceConfiguration + * @description Lecture de la configuration d'un service + * @param {object} serviceAdminConf - Configuration du service du point de vue de l'administrateur + * @return {json} configuration - Configuration du service + * + */ + + readServiceConfiguration(serviceAdminConf) { + + LOGGER.info(`readServiceConfiguration...`); + + LOGGER.debug(`Lecture fichier de configuration du service : ${serviceAdminConf.id}`); + + try { + + const configurationLocation = path.resolve(path.dirname(this._configurationPath), serviceAdminConf.configuration); + LOGGER.debug("Location à lire : " + configurationLocation); + + const configuration = JSON.parse(fs.readFileSync(configurationLocation)); + LOGGER.debug(configuration); + + return configuration; + + } catch (error) { + throw errorManager.createError(`Can't read service configuration file : ${error}`, 500); + } + } } diff --git a/src/js/apis/admin/1.0.0/controller/controller.js b/src/js/apis/admin/1.0.0/controller/controller.js index 41eddac..99dd146 100644 --- a/src/js/apis/admin/1.0.0/controller/controller.js +++ b/src/js/apis/admin/1.0.0/controller/controller.js @@ -3,6 +3,7 @@ const errorManager = require('../../../../utils/errorManager'); const log4js = require('log4js'); const HealthRequest = require('../../../../requests/healthRequest'); +const ServiceRequest = require('../../../../requests/serviceRequest'); var LOGGER = log4js.getLogger("CONTROLLER"); @@ -13,7 +14,7 @@ module.exports = { * @function * @name checkHealthParameters * @description Vérification des paramètres d'une requête sur /health - * @param {object} parameters - ensemble des paramètres de la requête + * @param {object} parameters - ensemble des paramètres de la requête ExpressJS * @return {object} HealthRequest - Instance de la classe HealthRequest * */ @@ -78,6 +79,37 @@ module.exports = { return userResponse; + }, + + /** + * + * @function + * @name checkServiceParameters + * @description Vérification des paramètres d'une requête sur /services/{service} + * @param {object} parameters - ensemble des paramètres de la requête ExpressJS + * @return {ServiceRequest} request - Instance de la classe ServiceRequest + * + */ + + checkServiceParameters: function(parameters) { + + LOGGER.debug("checkServiceParameters()"); + + // Service + if (!parameters.service) { + throw errorManager.createError(" Parameter 'service' is invalid: there is no value", 400); + } + + if (parameters.service === "") { + throw errorManager.createError(" Parameter 'service' is invalid: value should not be empty", 400); + } + + // TODO : vérifier ici que le service exite (appel à une fonction de la classe administrator) + + const request = new ServiceRequest(parameters.service); + + return request; + } } \ No newline at end of file diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index 30c5d79..17d7af4 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -52,7 +52,7 @@ router.route("/health") // Vérification des paramètres de la requête const healthRequest = controller.checkHealthParameters(parameters); LOGGER.debug(healthRequest); - // Envoie au service et récupération de l'objet réponse + // Envoie à l'administrateur et récupération de l'objet réponse const healthResponse = await administrator.computeHealthRequest(healthRequest); LOGGER.debug(healthResponse); // Formattage de la réponse @@ -80,15 +80,46 @@ router.route("/services") // On récupère l'instance d'Administrator pour répondre aux requêtes let administrator = req.app.get("administrator"); + try { + + const servicesResponse = administrator.getServicesConfigurations() + res.set('content-type', 'application/json'); + res.status(200).json(servicesResponse); + + } catch (error) { + return next(error); + } + + }); + +// Services/{service} +// Récupérer les informations d'un service +router.route("/services/:service") + + .get(async function(req, res, next) { + + LOGGER.debug("requete GET sur /admin/1.0.0/services/:service?"); + LOGGER.debug(req.originalUrl); + + // On récupère l'instance d'Administrator pour répondre aux requêtes + let administrator = req.app.get("administrator"); + // on récupère l'ensemble des paramètres de la requête - let parameters = req.query; + const parameters = req.params; LOGGER.debug(parameters); try { - const servicesResponse = administrator.getServicesConfigurations(parameters) + // Vérification des paramètres de la requête + const serviceRequest = controller.checkServiceParameters(parameters); + LOGGER.debug(serviceRequest); + + // Envoie à l'administrateur et récupération de l'objet réponse + const serviceResponse = administrator.getServiceConfiguration(serviceRequest.service); + + // Formattage de la réponse res.set('content-type', 'application/json'); - res.status(200).json(servicesResponse); + res.status(200).json(serviceResponse); } catch (error) { return next(error); diff --git a/src/js/requests/serviceRequest.js b/src/js/requests/serviceRequest.js new file mode 100644 index 0000000..d20ee1a --- /dev/null +++ b/src/js/requests/serviceRequest.js @@ -0,0 +1,56 @@ +'use strict'; + +const Request = require('./request'); + +/** +* +* @class +* @name serviceRequest +* @description Classe modélisant une requête sur un service géré par l'administrateur. +* +*/ + +module.exports = class serviceRequest extends Request { + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe serviceRequest + * @param {string} serviceId - Id du service interrogé + * + */ + + constructor(serviceId) { + + super("service", "serviceRequest"); + + // Id du service d'après l'administrateur + this._service = serviceId; + + } + + /** + * + * @function + * @name get service + * @description Récupérer le caractère verbeux de la requête + * + */ + get service() { + return this._service; + } + + /** + * + * @function + * @name set service + * @description Attribuer le caractère verbeux de la requête. + * @param {string} id - Id du service + * + */ + set service(id) { + this._service = id; + } + +} diff --git a/test/functional/request/cucumber/configurations/local-admin.json b/test/functional/request/cucumber/configurations/local-admin.json index c65a56a..9e227ed 100644 --- a/test/functional/request/cucumber/configurations/local-admin.json +++ b/test/functional/request/cucumber/configurations/local-admin.json @@ -7,7 +7,8 @@ "1.0.0": { "health": "/admin/1.0.0/health", "version": "/admin/1.0.0/version", - "services": "/admin/1.0.0/services" + "services": "/admin/1.0.0/services", + "services/": "/admin/1.0.0/services/" } } }, diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index 6766bbd..ba89eac 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -82,3 +82,22 @@ Feature: Road2 with data When I send the request Then the server should send a response with status 404 And the response should contain "Not found" + + Scenario: [admin/1.0.0] Configuration du service "main" + Given an "GET" request on operation "services/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | main | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain "application" + + Scenario: [admin/1.0.0] Configuration d'un service inexistant ne marche pas + Given an "GET" request on operation "services/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | other | + When I send the request + Then the server should send a response with status 404 + And the response should contain "Can't find service" diff --git a/test/functional/request/cucumber/features/support/steps.js b/test/functional/request/cucumber/features/support/steps.js index 840b388..7facfe3 100644 --- a/test/functional/request/cucumber/features/support/steps.js +++ b/test/functional/request/cucumber/features/support/steps.js @@ -34,6 +34,10 @@ Given('with query parameters:', function (table) { this.setQueryParameters(table.hashes()); }); +Given('with path parameters:', function (table) { + this.setPathParameters(table.hashes()); +}); + Given('with table parameters for {string}:', function (key, table) { this.setTableParameters(key, table.hashes()); }); diff --git a/test/functional/request/cucumber/features/support/world.js b/test/functional/request/cucumber/features/support/world.js index 7c7c281..dcc83b5 100644 --- a/test/functional/request/cucumber/features/support/world.js +++ b/test/functional/request/cucumber/features/support/world.js @@ -174,6 +174,16 @@ class road2World { } + setPathParameters(parametersToAdd) { + + for(let i = 0; i < parametersToAdd.length; i++) { + if (this._path.includes(`<${parametersToAdd[i].key}>`)) { + this._path = this._path.replace(`<${parametersToAdd[i].key}>`, parametersToAdd[i].value) + } + } + + } + setTableParameters(key, valuesToAdd) { let arrayParameters = new Array(); diff --git a/test/integration/mocha/requests/integrationServiceRequest.js b/test/integration/mocha/requests/integrationServiceRequest.js new file mode 100644 index 0000000..1fc3919 --- /dev/null +++ b/test/integration/mocha/requests/integrationServiceRequest.js @@ -0,0 +1,31 @@ +const assert = require('assert'); +const ServiceRequest = require('../../../../src/js/requests/serviceRequest'); +const logManager = require('../logManager'); + +describe('Test de la classe ServiceRequest', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let request = new ServiceRequest("test"); + + describe('Test du constructeur et des getters/setters', function() { + + it('Get type', function() { + assert.equal(request.type, "serviceRequest"); + }); + + it('Get service', function() { + assert.equal(request.service, "test"); + }); + + it('Set service', function() { + request.service = "main"; + assert.equal(request.service, "main"); + }); + + }); + +}); diff --git a/test/integration/mocha/responses/integrationHealthResponse.js b/test/integration/mocha/responses/integrationHealthResponse.js index 31796ce..3781e4e 100644 --- a/test/integration/mocha/responses/integrationHealthResponse.js +++ b/test/integration/mocha/responses/integrationHealthResponse.js @@ -1,5 +1,5 @@ const assert = require('assert'); -const HealthResponse = require('../../../../src/js/requests/healthResponse'); +const HealthResponse = require('../../../../src/js/responses/healthResponse'); const logManager = require('../logManager'); describe('Test de la classe HealthResponse', function() { @@ -40,7 +40,7 @@ describe('Test de la classe HealthResponse', function() { }); it('Set serviceStates', function() { - response.serviceStates.push = {"serviceId":"unknown","state":"green"}; + response.serviceStates.push({"serviceId":"unknown","state":"green"}); assert.deepEqual(response.serviceStates[0], {"serviceId":"unknown","state":"green"}); }); From 312ee18e8a2103ab690057aa5ffc2b5a62db3f4b Mon Sep 17 00:00:00 2001 From: lgrd Date: Wed, 15 Mar 2023 16:01:11 +0100 Subject: [PATCH 78/93] [feat] create admin without services (#51) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] init admin without services * [fix] update changelog * [doc] précision dans la doc dev --- changelog.md | 4 +- documentation/developers/concepts.md | 1 + package.json | 4 +- src/js/administrator/administrator.js | 332 ++++++++++++++------------ src/js/road2.js | 6 +- 5 files changed, 183 insertions(+), 164 deletions(-) diff --git a/changelog.md b/changelog.md index 96ce9eb..2bbe528 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ADDED: - Ajout de la route GET /admin/1.0.0/services dans l'API d'administration + - Ajout de la route GET /admin/1.0.0/services/{servcie} dans l'API d'administration + - Il est maintenant possible démarrer un administrateur sans services pré-configurés CHANGED: - La documentation de l'API d'administration a été grandement enrichie. @@ -11,7 +13,7 @@ CHANGED: - Les dossiers de sources et de resources des services peuvent maintenant être vide à l'initialisation. DELETED: - - option onStart inside admin configuration is deleted + - L'option onStart de la configuration admin est supprimée ## 2.0.0 diff --git a/documentation/developers/concepts.md b/documentation/developers/concepts.md index b25296f..582edc3 100644 --- a/documentation/developers/concepts.md +++ b/documentation/developers/concepts.md @@ -129,6 +129,7 @@ Le premier point d'entrée possible est le fichier `src/js/road2.js`. Ce fichier Cet administrateur permet plusieurs choses : - On peut le lancer uniquement pour vérifier la bonne configuration de l'administrateur et des services associés. Dans ce cas là, le processus s'éteint après la vérification et renvoie un code d'erreur permettant de déterminer s'il y a eu un problème et son type. - On peut le lancer en mode serveur pour administrer un ou plusieurs services via une API HTTP(S). Dans ce cas là, l'administrateur va lancer tous les services déjà configurés. Il sera aussi possible d'en créer d'autres plus tard. +- On peut créer un administrateur sans configuerer un service. Il sera possible de les configurer plus tard. Un administrateur a été créé pour réaliser des tâches qui auraient gêné la bonne exécution du service. diff --git a/package.json b/package.json index d612e10..677658b 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "road2", - "version": "2.0.0", + "version": "2.1.0-DEVELOP", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", "main": "src/js/road2.js", "scripts": { - "start": "node ./src/js/road2.js", + "start": "env NODE_ENV=prod node ./src/js/road2.js", "configCheck": "node ./src/js/road2.js --configCheck", "utest": "mocha --recursive './test/unit/mocha/**/*.js'", "itest": "mocha --recursive './test/integration/mocha/**/*.js'", diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 7f41136..396127a 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -100,134 +100,134 @@ module.exports = class Administrator { LOGGER.debug("configuration.administration.services est bien un tableau"); if (configuration.administration.services.length === 0) { - LOGGER.error("Mauvaise configuration: 'administration.services' est un tableau vide."); - return false; + LOGGER.warn("'administration.services' est un tableau vide."); } else { LOGGER.debug("configuration.administration.services est bien un tableau qui contient des éléments"); - } - } - } + // On ne veut pas que les Id soient réutilisés + let checkedServiceId = new Array(); + let checkedServiceConf = new Array(); + let checkedServiceConfLocation = new Array(); - // On ne veut pas que les Id soient réutilisés - let checkedServiceId = new Array(); - let checkedServiceConf = new Array(); - let checkedServiceConfLocation = new Array(); + for (let i = 0; i < configuration.administration.services.length; i++) { - for (let i = 0; i < configuration.administration.services.length; i++) { + let curServiceConf = configuration.administration.services[i]; - let curServiceConf = configuration.administration.services[i]; + let configurationLocation = ""; + let configurationContent = {}; - let configurationLocation = ""; - let configurationContent = {}; - - if (!curServiceConf.id) { - LOGGER.error("Mauvaise configuration: 'id' absent."); - return false; - } else { - - if (typeof(curServiceConf.id) !== "string") { - LOGGER.error("Mauvaise configuration: 'id' n'est pas une string."); - return false; - } - if (curServiceConf.id === "") { - LOGGER.error("Mauvaise configuration: 'id' est une string vide."); - return false; - } + if (!curServiceConf.id) { + LOGGER.error("Mauvaise configuration: 'id' absent."); + return false; + } else { - LOGGER.debug("Id présent : " + curServiceConf.id); + if (typeof(curServiceConf.id) !== "string") { + LOGGER.error("Mauvaise configuration: 'id' n'est pas une string."); + return false; + } + if (curServiceConf.id === "") { + LOGGER.error("Mauvaise configuration: 'id' est une string vide."); + return false; + } - // On vérifie que l'id n'est pas déjà pris - if (checkedServiceId.length === 0) { - // On continue la suite de la vérification - } else { + LOGGER.debug("Id présent : " + curServiceConf.id); - for (let j = 0; j < checkedServiceId.length; j++) { - if (curServiceConf.id === checkedServiceId[j]) { - LOGGER.error("Id de service déjà pris : " + curServiceConf.id); - return false; - } - } + // On vérifie que l'id n'est pas déjà pris + if (checkedServiceId.length === 0) { + // On continue la suite de la vérification + } else { - } + for (let j = 0; j < checkedServiceId.length; j++) { + if (curServiceConf.id === checkedServiceId[j]) { + LOGGER.error("Id de service déjà pris : " + curServiceConf.id); + return false; + } + } - } + } - if (!curServiceConf.configuration) { - LOGGER.error("Mauvaise configuration: 'configuration' absent."); - return false; - } else { - - LOGGER.debug("configuration présent"); - - try { - configurationLocation = path.resolve(path.dirname(this._configurationPath), curServiceConf.configuration); - LOGGER.debug("Chemin absolu du fichier de configuration du service : " + configurationLocation); - } catch (error) { - LOGGER.error("Can't get absolute path of service configuration: " + curServiceConf.configuration); - LOGGER.error(error); - return false; - } + } - // On vérifie que ce chemin n'est pas déjà utilisé - if (checkedServiceConfLocation.length !== 0) { - for (let cs = 0; cs < checkedServiceConfLocation.length; cs++) { - if (configurationLocation === checkedServiceConfLocation[cs]) { - LOGGER.error("La configuration indiquée est déjà vérifiée. Elle ne peut être utilisée pour un autre service géré par cet administrateur."); + if (!curServiceConf.configuration) { + LOGGER.error("Mauvaise configuration: 'configuration' absent."); return false; + } else { + + LOGGER.debug("configuration présent"); + + try { + configurationLocation = path.resolve(path.dirname(this._configurationPath), curServiceConf.configuration); + LOGGER.debug("Chemin absolu du fichier de configuration du service : " + configurationLocation); + } catch (error) { + LOGGER.error("Can't get absolute path of service configuration: " + curServiceConf.configuration); + LOGGER.error(error); + return false; + } + + // On vérifie que ce chemin n'est pas déjà utilisé + if (checkedServiceConfLocation.length !== 0) { + for (let cs = 0; cs < checkedServiceConfLocation.length; cs++) { + if (configurationLocation === checkedServiceConfLocation[cs]) { + LOGGER.error("La configuration indiquée est déjà vérifiée. Elle ne peut être utilisée pour un autre service géré par cet administrateur."); + return false; + } + } + } + + try { + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + configurationContent = JSON.parse(fs.readFileSync(configurationLocation)); + LOGGER.debug("Le contenu du fichier est accessible par Road2"); + } catch (error) { + LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de configuration du service: " + configurationLocation); + LOGGER.error(error); + return false; + } + + // On vérifie que ce contenu n'est pas déjà utilisé + if (checkedServiceConf.length !== 0) { + for (let cs = 0; cs < checkedServiceConf.length; cs++) { + try { + assert.deepStrictEqual(configurationContent, checkedServiceConf[cs]); + LOGGER.error("Le contenu de configuration indiqué est déjà vérifié pour un autre service. Il ne peut être utilisée pour plus d'un service géré par cet administrateur."); + return false; + } catch (err) { + LOGGER.debug("Les deux configuration de service ne sont pas identiques."); + } + } + } + + + } - } - } - - try { - // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard - configurationContent = JSON.parse(fs.readFileSync(configurationLocation)); - LOGGER.debug("Le contenu du fichier est accessible par Road2"); - } catch (error) { - LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de configuration du service: " + configurationLocation); - LOGGER.error(error); - return false; - } - - // On vérifie que ce contenu n'est pas déjà utilisé - if (checkedServiceConf.length !== 0) { - for (let cs = 0; cs < checkedServiceConf.length; cs++) { - try { - assert.deepStrictEqual(configurationContent, checkedServiceConf[cs]); - LOGGER.error("Le contenu de configuration indiqué est déjà vérifié pour un autre service. Il ne peut être utilisée pour plus d'un service géré par cet administrateur."); + + if (!curServiceConf.creationType) { + LOGGER.error("Mauvaise configuration: 'creationType' absent."); return false; - } catch (err) { - LOGGER.debug("Les deux configuration de service ne sont pas identiques."); + } else { + + LOGGER.debug("configuration.creationType présent"); + + if (!["sameProcess","newProcess","findByURI"].includes(curServiceConf.creationType)) { + LOGGER.error("Mauvaise configuration: 'creationType' doit être parmi 'sameProcess', 'newProcess', 'findByURI'."); + return false; + } else { + LOGGER.debug("configuration.creationType bien configuré"); + } + } + + LOGGER.debug("Vérification du service en cours terminée"); + checkedServiceId.push(curServiceConf.id); + checkedServiceConf.push(configurationContent); + checkedServiceConfLocation.push(configurationLocation); + + } - } - - - } - - if (!curServiceConf.creationType) { - LOGGER.error("Mauvaise configuration: 'creationType' absent."); - return false; - } else { - - LOGGER.debug("configuration.creationType présent"); - - if (!["sameProcess","newProcess","findByURI"].includes(curServiceConf.creationType)) { - LOGGER.error("Mauvaise configuration: 'creationType' doit être parmi 'sameProcess', 'newProcess', 'findByURI'."); - return false; - } else { - LOGGER.debug("configuration.creationType bien configuré"); } - - } - - LOGGER.debug("Vérification du service en cours terminée"); - checkedServiceId.push(curServiceConf.id); - checkedServiceConf.push(configurationContent); - checkedServiceConfLocation.push(configurationLocation); - + } } LOGGER.debug("Vérification des services terminée"); @@ -380,6 +380,11 @@ module.exports = class Administrator { LOGGER.info("Vérification de la configuration des services..."); + if (this._configuration.administration.services.length === 0) { + LOGGER.warn("Aucun service à vérifier"); + return true; + } + for (let i = 0; i < this._configuration.administration.services.length; i++) { let curIASConf = this._configuration.administration.services[i]; @@ -414,7 +419,12 @@ module.exports = class Administrator { async createServices() { - LOGGER.info("Vérification et création des services"); + LOGGER.info("Vérification et création des services..."); + + if (this._configuration.administration.services.length === 0) { + LOGGER.warn("Aucun service à créer"); + return true; + } // Pour chaque service, on vérifie sa configuration puis on le démarre @@ -480,64 +490,70 @@ module.exports = class Administrator { // Dans ce cas là, il faudra que l'administrator ait un attribut d'état et que celui-ci soit lu dans cette fonction healthResponse.adminState = "green"; - // Pour chaque service, on demande au serviceManager l'état du service - for (let i = 0; i < this._configuration.administration.services.length; i++) { + if (this._configuration.administration.services.length === 0) { + LOGGER.info("Aucun service, donc pas de status à récupérer"); + } else { - let curServiceId = this._configuration.administration.services[i].id; - LOGGER.debug("Demande de l'état du service : " + curServiceId); + // Pour chaque service, on demande au serviceManager l'état du service + for (let i = 0; i < this._configuration.administration.services.length; i++) { - // Le passage potentiel par IPC fait perdre les méthodes donc dans la suite, on est obligé de prendre les attributs avec _ - let curHealthResponse = await this._serviceManager.computeRequest(curServiceId, healthRequest); + let curServiceId = this._configuration.administration.services[i].id; + LOGGER.debug("Demande de l'état du service : " + curServiceId); - if (curHealthResponse._type !== "healthResponse") { - // Ce n'est pas normal, on renvoit une erreur pour ce service - // et on met le flag rouge - LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse du bon type"); - gotRed = true; - healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); - continue; - } + // Le passage potentiel par IPC fait perdre les méthodes donc dans la suite, on est obligé de prendre les attributs avec _ + let curHealthResponse = await this._serviceManager.computeRequest(curServiceId, healthRequest); - if (!curHealthResponse._serviceStates[0]) { - // Ce n'est pas normal, on renvoit une erreur pour ce service - // et on met le flag rouge - LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse"); - gotRed = true; - healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); - continue; - } + if (curHealthResponse._type !== "healthResponse") { + // Ce n'est pas normal, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse du bon type"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } - if (!curHealthResponse._serviceStates[0].state) { - // Ce n'est pas normal, on renvoit une erreur pour ce service - // et on met le flag rouge - LOGGER.error("Le service " + curServiceId + " n'a pas donné d'état"); - gotRed = true; - healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); - continue; - } + if (!curHealthResponse._serviceStates[0]) { + // Ce n'est pas normal, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " n'a pas donné de réponse"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } - // Pour la suite, on note la présence d'orange et de rouge dans les services - if (curHealthResponse._serviceStates[0].state === "orange") { - LOGGER.debug("Le service " + curServiceId + " est orange"); - gotOrange = true; - } else if (curHealthResponse._serviceStates[0].state === "red") { - LOGGER.debug("Le service " + curServiceId + " est red"); - gotRed = true; - } else if (curHealthResponse._serviceStates[0].state === "green") { - // Tout va bien, rien à faire - LOGGER.debug("Le service " + curServiceId + " est green"); - } else { - // Cela ne devrait pas arriver, on renvoit une erreur pour ce service - // et on met le flag rouge - LOGGER.error("Le service " + curServiceId + " est dans un état inconnu"); - gotRed = true; - healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); - continue; - } + if (!curHealthResponse._serviceStates[0].state) { + // Ce n'est pas normal, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " n'a pas donné d'état"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } + + // Pour la suite, on note la présence d'orange et de rouge dans les services + if (curHealthResponse._serviceStates[0].state === "orange") { + LOGGER.debug("Le service " + curServiceId + " est orange"); + gotOrange = true; + } else if (curHealthResponse._serviceStates[0].state === "red") { + LOGGER.debug("Le service " + curServiceId + " est red"); + gotRed = true; + } else if (curHealthResponse._serviceStates[0].state === "green") { + // Tout va bien, rien à faire + LOGGER.debug("Le service " + curServiceId + " est green"); + } else { + // Cela ne devrait pas arriver, on renvoit une erreur pour ce service + // et on met le flag rouge + LOGGER.error("Le service " + curServiceId + " est dans un état inconnu"); + gotRed = true; + healthResponse.serviceStates.push({"serviceId":curServiceId,"state":"unknown"}); + continue; + } - // On stocke le retour de ce service - curHealthResponse._serviceStates[0].id = curServiceId; - healthResponse.serviceStates.push(curHealthResponse._serviceStates[0]); + // On stocke le retour de ce service + curHealthResponse._serviceStates[0].id = curServiceId; + healthResponse.serviceStates.push(curHealthResponse._serviceStates[0]); + + } } diff --git a/src/js/road2.js b/src/js/road2.js index 6cdf6ed..b6342d6 100644 --- a/src/js/road2.js +++ b/src/js/road2.js @@ -68,7 +68,7 @@ async function start() { LOGGER.error("Problèmes lors du démarrage des services. Reconfigurez les et relancez leur démarrage."); // On n'éteint pas le serveur d'administration car les services pourront être reconfiguré et démarrés par l'API } else { - LOGGER.info("Les services ont été démarré"); + LOGGER.info("S'il y en a, les services ont été démarré"); } } @@ -102,7 +102,7 @@ async function start() { pm.shutdown(1); } else { - LOGGER.info("La configuration des services est validée"); + LOGGER.info("S'il y en a, la configuration des services est validée"); } } @@ -204,7 +204,7 @@ function checkAndInitLogger(userLogConfiguration) { } //Instanciation du logger - LOGGER = log4js.getLogger('SERVER'); + LOGGER = log4js.getLogger('ROAD2'); LOGGER.info("Logger charge."); From 47188a10c22f2a31f3c4fbe956d21a2732127716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Blanc?= Date: Fri, 24 Mar 2023 15:24:51 +0100 Subject: [PATCH 79/93] feat(admin): add route to restart service (#53) * feat(admin): add route to restart service * feat(test): add cucumber test for route to restart service * feat(admin): use promises to wait for service completely stopped * feat(admin): use async/await and promises to wait for servers completely started/stopped * wip: update api responses, promise use, changelog and tests * [fix] api doc and wait for newProcess * [test] update itest and utest for async functions * [feat] check the conf before asking a restart --------- Co-authored-by: Loic --- changelog.md | 3 +- .../apis/administration/1.0.0/api.yaml | 44 ++++++++++- src/js/administrator/administrator.js | 61 ++++++++++++++ src/js/apis/admin/1.0.0/index.js | 54 +++++++++++-- src/js/server/server.js | 42 ++++++++-- src/js/server/serverManager.js | 67 ++++++++++++---- src/js/service/main.js | 18 +++-- src/js/service/service.js | 31 ++++++-- src/js/service/serviceAdministered.js | 11 +++ src/js/service/serviceInsider.js | 41 +++++++++- src/js/service/serviceManager.js | 76 +++++++++++++++++- src/js/service/serviceProcess.js | 79 ++++++++++++++++--- .../cucumber/configurations/local-admin.json | 3 +- .../cucumber/features/req-admin-1.0.0.feature | 19 +++++ .../mocha/server/integrationServerManager.js | 10 ++- .../mocha/service/integrationService.js | 2 +- test/unit/mocha/server/testsServer.js | 20 +++-- 17 files changed, 505 insertions(+), 76 deletions(-) diff --git a/changelog.md b/changelog.md index 2bbe528..2e00f61 100644 --- a/changelog.md +++ b/changelog.md @@ -4,7 +4,8 @@ ADDED: - Ajout de la route GET /admin/1.0.0/services dans l'API d'administration - - Ajout de la route GET /admin/1.0.0/services/{servcie} dans l'API d'administration + - Ajout de la route GET /admin/1.0.0/services/{service} dans l'API d'administration + - Ajout de la route GET /admin/1.0.0/services/{service}/restart dans l'API d'administration - Il est maintenant possible démarrer un administrateur sans services pré-configurés CHANGED: diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index e3294fb..c9eaab3 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -348,7 +348,49 @@ paths: application/json: schema: $ref: "#/components/schemas/errorResponse" - + + /services/{service}/restart: + get: + tags: + - "Gestion des services" + summary: "Demander le redémarage d'un service." + description: | + Cette requête demande le redémarage du service demandé. Cette demande peut + échouer et l'utilisateur en sera informé. + operationId: "get-service-restart" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/serviceConfiguration" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 500: + description: "Internal server error" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + /services/{service}/resources: get: tags: diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 396127a..49084f4 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -676,6 +676,67 @@ module.exports = class Administrator { } + /** + * + * @function + * @name restartService + * @description Redémarrage d'un service + * @param {string} serviceId - Identifiant du service qu'on veut redémarrer + * @return {boolean} status - Retourne si le service a bien été redémarré + * + */ + + async restartService(serviceId) { + + LOGGER.info("restartService..."); + + // On récupère la configuration admin de ce service, s'il existe + const serviceAdminConf = this._configuration.administration.services.find(service => service.id == serviceId); + + if (serviceAdminConf) { + + LOGGER.debug("La configuration admin du service " + serviceId + " a été trouvée"); + LOGGER.debug(serviceAdminConf); + + // Récupération de la configuration + LOGGER.info("Récupération de la configuration du service"); + let serviceConfLocation = ""; + let serviceConfiguration = {}; + + try { + serviceConfLocation = path.resolve(path.dirname(this._configurationPath), serviceAdminConf.configuration); + serviceConfiguration = JSON.parse(fs.readFileSync(serviceConfLocation)); + } catch (error) { + LOGGER.error("Impossible de récupérer la configuration du service : " + error); + return false; + } + + + // On vérifie la configuration avant de redémarrer afin d'éviter au maximum les mauvaises surprises + LOGGER.info("Vérification de la configuration avant de demander un redémarrage"); + if (!(await this._serviceManager.checkServiceConfiguration(serviceConfiguration, serviceConfLocation))) { + LOGGER.error("La configuration du service "+ serviceId +" est incorrecte donc il ne peut être redémarré"); + return false; + } else { + LOGGER.info("La configuration du service "+ serviceId +" est correcte donc il peut être redémarré"); + } + + const options = {adminLogConfiguration: this._logConfiguration}; + if (!await this._serviceManager.restartService(serviceAdminConf.creationType, serviceAdminConf.id, serviceConfLocation, options)) { + LOGGER.error("Impossible de redémarer le service "+ serviceAdminConf.id); + return false; + } else { + LOGGER.info("Le service " + serviceAdminConf.id + " a été redémarré correctement"); + } + + } else { + throw errorManager.createError(`Can't find service ${serviceId}`, 404); + } + + return true; + + } + } diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index 17d7af4..19fa757 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -4,6 +4,7 @@ const express = require('express'); const log4js = require('log4js'); const packageJSON = require('../../../../../package.json'); +const errorManager = require('../../../utils/errorManager'); const controller = require('./controller/controller'); var LOGGER = log4js.getLogger("ADMIN"); @@ -127,6 +128,48 @@ router.route("/services/:service") }); +// Services/{service}/restart +// Récupérer les informations d'un service +router.route("/services/:service/restart") + + .get(async function(req, res, next) { + + LOGGER.debug("requete GET sur /admin/1.0.0/services/:service/restart"); + LOGGER.debug(req.originalUrl); + + // On récupère l'instance d'Administrator pour répondre aux requêtes + let administrator = req.app.get("administrator"); + + // on récupère l'ensemble des paramètres de la requête + const parameters = req.params; + LOGGER.debug(parameters); + + try { + + // Vérification des paramètres de la requête + const serviceRequest = controller.checkServiceParameters(parameters); + LOGGER.debug(serviceRequest); + + // Envoie à l'administrateur et récupération de l'objet réponse + const restartStatus = await administrator.restartService(serviceRequest.service); + + if (restartStatus) { + + const serviceResponse = administrator.getServiceConfiguration(serviceRequest.service); + // Formattage de la réponse + res.set('content-type', 'application/json'); + res.status(200).json(serviceResponse); + + } else { + next(errorManager.createError("Unknown error during the reload")); + } + + } catch (error) { + return next(error); + } + + }); + // Gestion des erreurs // Cette partie doit être placée après la définition des routes normales // --- @@ -151,7 +194,6 @@ function logError(err, req, res, next) { query: req.query, body: req.body, error: { - errorType: err.code, message: err.message, stack: err.stack } @@ -179,15 +221,15 @@ function sendError(err, req, res, next) { if (err.status) { // S'il y a un status dans le code, alors cela veut dire qu'on veut remonter l'erreur au client res.status(err.status); - res.json({ error: {errorType: err.code, message: err.message}}); + res.json({ error: {message: err.message}}); } else { // S'il n'y a pas de status dans le code alors on ne veut pas remonter l'erreur res.status(500); - res.json({ error: {errorType: "internal", message: "Internal Server Error"}}); + res.json({ error: {message: "Internal Server Error"}}); } } else if ((process.env.NODE_ENV === "debug")) { res.status(err.status || 500); - res.json({ error: {errorType: err.code, + res.json({ error: { message: err.message, stack: err.stack, // utile lorsqu'une erreur sql remonte @@ -196,7 +238,7 @@ function sendError(err, req, res, next) { } else { // En dev, on veut faire remonter n'importe quelle erreur res.status(err.status || 500); - res.json({ error: {errorType: err.code, message: err.message}}); + res.json({ error: {message: err.message}}); } } @@ -211,7 +253,7 @@ function sendError(err, req, res, next) { function notFoundError(req, res) { res.status(404); - res.send({ error: "Not found" }); + res.send({ error: { message: "Not found" }}); } module.exports = router; diff --git a/src/js/server/server.js b/src/js/server/server.js index 6c07f69..85a3b4b 100644 --- a/src/js/server/server.js +++ b/src/js/server/server.js @@ -97,7 +97,9 @@ module.exports = class Server { * @description Démarer un serveur * */ - start() { + async start() { + + LOGGER.info("Démarage du serveur..."); // si le serveur n'existe pas, on le crée try { @@ -107,6 +109,7 @@ module.exports = class Server { if (this._enableHttps === "true") { let options = {}; + // TODO : modifier cette partie pour distinguer les erreurs retournées par fs et http options.key = fs.readFileSync(this._options.key, "utf-8"); options.cert = fs.readFileSync(this._options.cert, "utf-8"); @@ -123,10 +126,22 @@ module.exports = class Server { } // on lance l'écoute du serveur - this._server.listen(this._port, this._host); - LOGGER.info(this._host + ":" + this._port); + return new Promise((resolve, reject) => { + + this._server.listen(this._port, this._host, (error) => { + + if (error) { + LOGGER.error("Erreur lors du démarage du serveur : " + error); + reject(false); + } else { + LOGGER.info(this._host + ":" + this._port); + resolve(true); + } + + }); + + }); - return true; } @@ -137,10 +152,23 @@ module.exports = class Server { * @description Arrêter un serveur * */ - stop(callback) { + stop() { + + return new Promise((resolve, reject) => { + + this._server.close((err) => { + + if (err) { + LOGGER.error(`Erreur lors de l'arrêt du serveur : ${err}`); + reject(false); + } else { + LOGGER.debug(`Le serveur a été arrêté.`); + resolve(true); + } + + }); - this._server.close(callback); - return true; + }); } diff --git a/src/js/server/serverManager.js b/src/js/server/serverManager.js index 7139a76..e487342 100644 --- a/src/js/server/serverManager.js +++ b/src/js/server/serverManager.js @@ -271,9 +271,10 @@ module.exports = class serverManager { * @function * @name startAllServers * @description Démarrer l'ensemble des serveurs disponibles dans le manager + * @return {boolean} * */ - startAllServers() { + async startAllServers() { LOGGER.info("Demarrage de l'ensemble des serveurs."); @@ -290,30 +291,47 @@ module.exports = class serverManager { // tout va bien } + let allServersStatus = true; + for (let serverId in this._serverCatalog) { + LOGGER.info("Serveur: " + serverId); - if (!this._serverCatalog[serverId].start()) { - LOGGER.error("Erreur lors du demarrage du serveur."); - return false; + try { + + if (!(await this._serverCatalog[serverId].start())) { + LOGGER.error(`Erreur lors du demarrage du serveur ${serverId}.`); + allServersStatus = false; + } else { + LOGGER.info(`Le serveur ${serverId} a démarré.`) + } + + } catch(error) { + // On récupère une erreur du start afin de continuer le démarage des autres + LOGGER.error("Le serveur a renvoyé une erreur : " + error); + continue; } + } - LOGGER.info("Les demarrages se sont bien deroules."); + if (allServersStatus) { + LOGGER.debug("Les demarrages de tous les serveurs se sont bien deroules."); + } - return true; + return allServersStatus; } /** * * @function - * @name stopAllServer + * @name stopAllServers * @description Arrêter l'ensemble des serveurs disponibles dans le manager + * @return {boolean} allServerStatus - Renvoie true si tous les serveurs se sont arrêtés correctement * */ - stopAllServer() { + async stopAllServers() { - LOGGER.info("Arret de l'ensemble des serveurs."); + LOGGER.info("Arret de l'ensemble des serveurs du service..."); if (this._loadedServerId.length === 0) { LOGGER.warn("Aucun serveur n'est disponible (id)."); @@ -328,17 +346,34 @@ module.exports = class serverManager { // On continue } + let allServersStatus = true; + for (let serverId in this._serverCatalog) { - LOGGER.info("Serveur: " + serverId); - if (!this._serverCatalog[serverId].stop()) { - LOGGER.error("Erreur lors de l'arret du serveur."); - return false; + + LOGGER.debug("Serveur: " + serverId); + + try { + + if (!(await this._serverCatalog[serverId].stop())) { + LOGGER.error(`Le serveur ${serverId} n'a pas été arrếté correctement`); + allServersStatus = false; + } else { + LOGGER.info(`Le serveur ${serverId} a été arrêté.`); + } + + } catch(error) { + // On récupère une erreur du stop afin de continuer l'arrêt des autres + LOGGER.error("Le serveur a renvoyé une erreur : " + error); + continue; } - } - LOGGER.info("Les arrets se sont bien deroules."); + } + + if (allServersStatus) { + LOGGER.debug("Les arrets de tous les serveurs se sont bien deroules."); + } - return true; + return allServersStatus; } diff --git a/src/js/service/main.js b/src/js/service/main.js index dbfa975..b23b6d4 100644 --- a/src/js/service/main.js +++ b/src/js/service/main.js @@ -80,15 +80,21 @@ async function startService() { LOGGER.info("Les sources connectables ont été connectées"); - // Instanciation de la fonction permettant de recevoir des messages via IPC + // On démarre les serveurs associé à ce service + if (!(await service.startServers())) { + LOGGER.fatal("Erreur lors du démarrage des serveurs"); + pm.shutdown(5); + } else { + LOGGER.info("Les serveurs ont bien été démarrés"); + } + + // Le service est prêt à traiter des requêtes + // Instanciation de la fonction permettant de recevoir des messages via IPC si besoin if (process.argv[3] === "child") { LOGGER.info("Ce service est un child d'un administrateur. Instanciation de la fonction permettant de recevoir les messages"); service.initIPC(); - } - - // On démarre les serveurs associé à ce service - if (!service.startServers()) { - pm.shutdown(5); + } else { + LOGGER.debug("Processus parent, aucune connexion IPC à gérer"); } } diff --git a/src/js/service/service.js b/src/js/service/service.js index 3c2b0a1..79ece38 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -887,12 +887,11 @@ module.exports = class Service { * @description Démarrage des serveurs du service * */ - startServers() { + async startServers() { LOGGER.info("Démarrage des serveurs du service..."); - // Démarrage des serveurs - if (!this._serverManager.startAllServers()) { + if (!(await this._serverManager.startAllServers())) { LOGGER.fatal("Impossible de démarrer tous les serveurs."); return false; } else { @@ -910,16 +909,18 @@ module.exports = class Service { * */ - stopServers() { + async stopServers() { - // Extinction des serveurs - if (!this._serverManager.stopAllServer()) { + LOGGER.info("Extinction des serveurs..."); + + if (!(await this._serverManager.stopAllServers())) { LOGGER.fatal("Impossible d'eteindre les serveurs."); return false; + } else { + LOGGER.info("Les serveurs du service sont éteints."); + return true; } - return true; - } /** @@ -1006,6 +1007,20 @@ module.exports = class Service { }); + process.on('SIGTERM', async () => { + + LOGGER.debug("Réception du signal SIGTERM pour arrêter le service"); + + if (await this.stopServers()) { + LOGGER.debug("Les serveurs sont bien arrêtés, on peut sortir du service (exit)") + process.exit(0); + } else { + LOGGER.fatal("Les serveurs ne se sont pas bien arrếtés"); + process.exit(1); + } + + }); + } /** diff --git a/src/js/service/serviceAdministered.js b/src/js/service/serviceAdministered.js index 1a06880..19b8262 100644 --- a/src/js/service/serviceAdministered.js +++ b/src/js/service/serviceAdministered.js @@ -63,6 +63,17 @@ module.exports = class ServiceAdministered { */ loadService() { + } + + /** + * + * @function + * @name stopService + * @description Fonction pour arrêter un service selon le mode adaptée à la classe fille. Elle doit être ré-écrite dans chaque classe fille. + * + */ + stopService() { + } /** diff --git a/src/js/service/serviceInsider.js b/src/js/service/serviceInsider.js index adc1b33..1c100bb 100644 --- a/src/js/service/serviceInsider.js +++ b/src/js/service/serviceInsider.js @@ -127,10 +127,22 @@ module.exports = class ServiceInsider extends ServiceAdministered { LOGGER.info("Les sources connectables ont été connectées"); - // On démarre les serveurs associé à ce service - if (!this._serviceInstance.startServers()) { - LOGGER.error("Impossible de démarrer les serveurs. Impossible de démarrer le service"); + // On connecte les sources + if (!(await this._serviceInstance.connectSources())) { + + LOGGER.error("Aucune source n'a pu être connectée"); return false; + + } else { + + LOGGER.info("Les sources connectables ont été connectées"); + + // On démarre les serveurs associé à ce service + if (!(await this._serviceInstance.startServers())) { + LOGGER.error("Impossible de démarrer les serveurs. Impossible de démarrer le service"); + return false; + } + } } @@ -143,6 +155,29 @@ module.exports = class ServiceInsider extends ServiceAdministered { } + /** + * + * @function + * @name stopService + * @description Arrêter un service administré via une instanciation de la classe Service + * @return {boolean} status - Retourne si le service a bien été arrêté + * + */ + async stopService() { + + LOGGER.debug("Arrêt d'un service dans le même processus"); + + if (await this._serviceInstance.stopServers()) { + LOGGER.debug("Service arrêté."); + this._serviceInstance = null; + return true; + } else { + LOGGER.error("Le service n'a pu être arrêté"); + return false; + } + + } + /** * * @function diff --git a/src/js/service/serviceManager.js b/src/js/service/serviceManager.js index 405249e..c8e150d 100644 --- a/src/js/service/serviceManager.js +++ b/src/js/service/serviceManager.js @@ -118,12 +118,80 @@ module.exports = class serviceManager { if (!(await serviceAdministered.loadService(options))) { LOGGER.error("Impossible de charger le service " + id); return false; + } else { + + // Sauvegarde du service dans le manager + LOGGER.info(`Service ${id} démarré. Sauvegarde dans le serviceManager...`); + this._loadedServiceAdministeredCatalog[id] = serviceAdministered; + this._loadedServiceConfLocations[id] = configurationLocation; + return true; + + } + + } + + /** + * + * @function + * @name stopService + * @description Arrêt d'un service + * @param {string} creationType - Type de création pour le service. Permet d'indiquer si on est dans le même process ou pas, voir sur une autre machine en théorie. + * @param {string} id - Id du service pour l'administrateur + * @param {string} configurationLocation - Emplacement de la configuration du service à charger + * @param {object} options - Contenu optionnel pour le chargement de certains types de services + * @return {boolean} response - Retourne si le service a bien été arrêté + * + */ + async stopService(id) { + + LOGGER.info(`Arrêt du service ${id} en cours...`); + + // On récupère le service administré + const administeredService = this._loadedServiceAdministeredCatalog[id]; + if (!administeredService) { + LOGGER.error("Aucun service associé à cet ID: " + serviceId); + return false; + } + + if (!(await administeredService.stopService())) { + LOGGER.error("Impossible d'arrêter le service " + id); + return false; + } + + return true; + + } + + /** + * + * @function + * @name restartService + * @description Redémarrage d'un service + * @param {string} creationType - Type de création pour le service. Permet d'indiquer si on est dans le même process ou pas, voir sur une autre machine en théorie. + * @param {string} id - Id du service pour l'administrateur + * @param {string} configurationLocation - Emplacement de la configuration du service à charger + * @param {object} options - Contenu optionnel pour le chargement de certains types de services + * + */ + async restartService(creationType, id, configurationLocation, options) { + + LOGGER.info("Redémarrage du service " + id); + + // Arrêt du service + if (!await this.stopService(id)) { + LOGGER.error("Impossible d'arrêter le service : " + id); + return false; + } else { + LOGGER.info("Le service " + id + " a été arrêté correctement"); } - // Sauvegarde du service dans le manager - LOGGER.info("Service démarré. Sauvegarde dans le serviceManager..."); - this._loadedServiceAdministeredCatalog[id] = serviceAdministered; - this._loadedServiceConfLocations[id] = configurationLocation; + // Lancement du service + if (!await this.loadService(creationType, id, configurationLocation, options)) { + LOGGER.error("Impossible de démarrer le service : " + id); + return false; + } else { + LOGGER.info("Le service " + id + " a été démarré correctement"); + } return true; diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js index a9ef1d0..8f67b41 100644 --- a/src/js/service/serviceProcess.js +++ b/src/js/service/serviceProcess.js @@ -5,10 +5,8 @@ const { fork } = require('child_process'); const ServiceAdministered = require('./serviceAdministered'); const errorManager = require('../utils/errorManager'); -const healthResponse = require('../responses/healthResponse'); -const { - setInterval, - } = require('node:timers/promises'); +const {setInterval} = require('node:timers/promises'); +const HealthRequest = require('../requests/healthRequest'); // Création du LOGGER const LOGGER = log4js.getLogger("SERVICEPRO"); @@ -59,21 +57,21 @@ module.exports = class ServiceProcess extends ServiceAdministered { * @description Créer un service administré via un fork de processus * */ - loadService() { + async loadService() { - LOGGER.info("Création et lancement d'un service dans un autre processus"); + LOGGER.info("Création et lancement d'un service dans un autre processus"); // Un minimum de vérifications au cas où if (typeof(this._configurationLocation) !== "string") { LOGGER.error("Le chemin fourni n'est pas une string"); - return null; + return false; } else { LOGGER.debug("Le chemin fourni est bien une string"); } if (this._configurationLocation === "") { LOGGER.error("Le chemin fourni est vide"); - return null; + return false; } else { LOGGER.debug("Le chemin est renseigné : " + this._configurationLocation); } @@ -93,7 +91,13 @@ module.exports = class ServiceProcess extends ServiceAdministered { // Création du service via un fork : on lance simplement service/main.js LOGGER.info("Fork du processus pour créer le service..."); - this._serviceInstance = fork("./src/js/service/main.js", [this._configurationLocation, "child"], serviceOptions); + try { + this._serviceInstance = fork("./src/js/service/main.js", [this._configurationLocation, "child"], serviceOptions); + } catch(error) { + LOGGER.error("Impossible de lancer un processus child : " + error); + this._serviceInstance = {}; + return false; + } LOGGER.info("Processus enfant créé"); @@ -104,20 +108,75 @@ module.exports = class ServiceProcess extends ServiceAdministered { LOGGER.debug(response); // On stocke la réponse - this._unReadResponses[response._uuid] = response; + if (response._uuid) { + this._unReadResponses[response._uuid] = response; + } else { + // TODO voir ce qu'on fait + } }); + // On demande au service (child) son état + // Cela permet de voir si le child est bien démarré et qu'il n'y ait eu pas d'erreur lors de son lancement + LOGGER.info("Attente de l'état du service"); + try { + let healthResponse = await this.computeRequest(new HealthRequest()); + LOGGER.debug("Le service a repondu : " + healthResponse); + } catch (error) { + LOGGER.error("Erreur lors de l'attente de l'état du service : " + error); + return false; + } + return true; } + /** + * + * @function + * @name stopService + * @description Arrêter un service administré via une instanciation de la classe Service + * @return {boolean} status - Retourne si le service s'est bien arrêté + * + */ + async stopService() { + + LOGGER.debug("Arrêt d'un service dans un autre processus"); + + // Envoi du signal SIGTERM + this._serviceInstance.kill(); + + // Toutes les 1s on va voir si le child s'est éteint + const interval = 1000; + + // On attend que le child soit killed pour récupérer son exit code + for await (const startTime of setInterval(interval, Date.now())) { + + let exitCode = this._serviceInstance.exitCode; + if (exitCode !== null) { + LOGGER.debug("Processus arrếté avec l'exit code : " + exitCode); + this._serviceInstance = {}; + return true; + } + + // Gestion du timeout : 20sec + const now = Date.now(); + if ((now - startTime) > 20000) { + LOGGER.info("Timeout atteint pour l'attente du code d'exit"); + return false; + } + + } + + } + /** * * @function * @name computeRequest * @description Fonction pour utiliser pour envoyer une requête à un service selon le mode adaptée à la classe fille. Elle doit être ré-écrite dans chaque classe fille. * @param {object} request - Instance fille de la classe Request + * @returns {object} response - Instance fille de la classe Response * */ async computeRequest(request) { diff --git a/test/functional/request/cucumber/configurations/local-admin.json b/test/functional/request/cucumber/configurations/local-admin.json index 9e227ed..aee46ec 100644 --- a/test/functional/request/cucumber/configurations/local-admin.json +++ b/test/functional/request/cucumber/configurations/local-admin.json @@ -8,7 +8,8 @@ "health": "/admin/1.0.0/health", "version": "/admin/1.0.0/version", "services": "/admin/1.0.0/services", - "services/": "/admin/1.0.0/services/" + "services/": "/admin/1.0.0/services/", + "services//restart": "/admin/1.0.0/services//restart" } } }, diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index ba89eac..2ea6b63 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -93,6 +93,25 @@ Feature: Road2 with data And the response should have an header "content-type" with value "application/json" And the response should contain "application" + Scenario: [admin/1.0.0] Redémarrage du service "main" + Given an "GET" request on operation "services//restart" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | main | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + + Scenario: [admin/1.0.0] Redémarrage d'un service inexistant + Given an "GET" request on operation "services//restart" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | test | + When I send the request + Then the server should send a response with status 404 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Can't find service" + Scenario: [admin/1.0.0] Configuration d'un service inexistant ne marche pas Given an "GET" request on operation "services/" in api "admin" "1.0.0" And with path parameters: diff --git a/test/integration/mocha/server/integrationServerManager.js b/test/integration/mocha/server/integrationServerManager.js index 93963b2..aaa3027 100644 --- a/test/integration/mocha/server/integrationServerManager.js +++ b/test/integration/mocha/server/integrationServerManager.js @@ -74,12 +74,14 @@ describe('Test de la classe ServerManager', function() { describe('Cycle de vie des serveurs gérés', function() { - it('startAllServers()', function() { - assert.equal(serverManager.startAllServers(), true); + it('startAllServers()', async function() { + let status = await serverManager.startAllServers(); + assert.equal(status, true); }); - it('stopAllServer()', function() { - assert.equal(serverManager.stopAllServer(), true); + it('stopAllServers()', async function() { + let status = await serverManager.stopAllServers(); + assert.equal(status, true); }); }); diff --git a/test/integration/mocha/service/integrationService.js b/test/integration/mocha/service/integrationService.js index 885e740..0ddb6db 100644 --- a/test/integration/mocha/service/integrationService.js +++ b/test/integration/mocha/service/integrationService.js @@ -86,7 +86,7 @@ describe('Test de la classe Service', function() { const serverManager = sinon.mock(ServerManager); serverManager.createAllServer = sinon.stub().returns(true); serverManager.startAllServer = sinon.stub().returns(true); - serverManager.stopAllServer = sinon.stub().returns(true); + serverManager.stopAllServers = sinon.stub().returns(true); serverManager.checkConfiguration = sinon.stub().returns(true); service._serverManager = serverManager; diff --git a/test/unit/mocha/server/testsServer.js b/test/unit/mocha/server/testsServer.js index 5b0c1c2..b76d49b 100644 --- a/test/unit/mocha/server/testsServer.js +++ b/test/unit/mocha/server/testsServer.js @@ -38,12 +38,14 @@ describe('Test de la classe Server', function() { assert.equal(server.id, httpServer.id); }); - it('Start()', function() { - assert.equal(server.start(), true); + it('Start()', async function() { + let status = await server.start(); + assert.equal(status, true); }); - it('Stop()', function() { - assert.equal(server.stop(), true); + it('Stop()', async function() { + let status = await server.stop(); + assert.equal(status, true); }); }); @@ -56,12 +58,14 @@ describe('Test de la classe Server', function() { assert.equal(server.id, httpsServer.id); }); - it('Start()', function() { - assert.equal(server.start(), true); + it('Start()', async function() { + let status = await server.start(); + assert.equal(status, true); }); - it('Stop()', function() { - assert.equal(server.stop(), true); + it('Stop()', async function() { + let status = await server.stop(); + assert.equal(status, true); }); }); From f4f870f4db9e653957fa4c55ebc2591533927f66 Mon Sep 17 00:00:00 2001 From: lgrd Date: Mon, 27 Mar 2023 11:40:46 +0200 Subject: [PATCH 80/93] [fix] pgrouting sources without attributes (#58) * [fix] handle cases wo attributes * wip: handling request and response * [fix] error in the doc * wip: ajout de rtest * fix doc: delete name --- .../apis/administration/1.0.0/api.yaml | 4 +- .../configuration/resources/pgr.resource | 1 - .../configuration/resources/smartpgr.resource | 1 - .../configuration/sources/pgr.source | 5 - src/js/sources/pgrSource.js | 177 ++++++++++-------- .../features/req-simple-1.0.0-pgr.feature | 118 +++++++++++- 6 files changed, 218 insertions(+), 88 deletions(-) diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index c9eaab3..2e582a2 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -1081,7 +1081,7 @@ components: example: "name" column: type: "string" - example: "way_names" + example: "concat_names" default: type: "string" example: "false" @@ -1155,7 +1155,7 @@ components: example: "name" column: type: "string" - example: "way_names" + example: "concat_names" default: type: "string" example: "false" diff --git a/documentation/configuration/resources/pgr.resource b/documentation/configuration/resources/pgr.resource index 67b2244..39e1f1d 100644 --- a/documentation/configuration/resources/pgr.resource +++ b/documentation/configuration/resources/pgr.resource @@ -61,7 +61,6 @@ { "id": "waysAttributes", "values": [ - "name", "nom_1_gauche", "nom_1_droite", "cpx_numero", diff --git a/documentation/configuration/resources/smartpgr.resource b/documentation/configuration/resources/smartpgr.resource index d0b3136..605be70 100644 --- a/documentation/configuration/resources/smartpgr.resource +++ b/documentation/configuration/resources/smartpgr.resource @@ -63,7 +63,6 @@ { "id": "waysAttributes", "values": [ - "name", "nom_1_gauche", "nom_1_droite", "cpx_numero", diff --git a/documentation/configuration/sources/pgr.source b/documentation/configuration/sources/pgr.source index c1f0ed6..856bb71 100644 --- a/documentation/configuration/sources/pgr.source +++ b/documentation/configuration/sources/pgr.source @@ -9,11 +9,6 @@ "dbConfig": "/home/docker/data/output_base.json", "schema": "public", "attributes": [ - { - "key": "name", - "column": "way_names", - "default": "false" - }, { "key": "nom_1_gauche", "column": "nom_1_gauche", diff --git a/src/js/sources/pgrSource.js b/src/js/sources/pgrSource.js index 5e27990..173c871 100644 --- a/src/js/sources/pgrSource.js +++ b/src/js/sources/pgrSource.js @@ -59,32 +59,37 @@ module.exports = class pgrSource extends Source { // Attributs disponibles sur les voies dans la base this._otherAttributes = new Array(); + // Coûts disponibles + this._costs = {}; + // TODO : à l'exemple des ressources, faire une fonction init() pour chaque source appelée dans le sourceManager - // Création des tableaux d'attributs - for (let i = 0; i < sourceJsonObject.storage.base.attributes.length; i++) { - let curAttribute = sourceJsonObject.storage.base.attributes[i]; - if (curAttribute.default === "true") { - this._defaultAttributes.push(curAttribute); - } else if (curAttribute.default === "false") { - this._otherAttributes.push(curAttribute); - } else { - // cela ne doit pas arriver + // -- + if (sourceJsonObject.storage.base.attributes) { + + // Création des tableaux d'attributs + for (let i = 0; i < sourceJsonObject.storage.base.attributes.length; i++) { + let curAttribute = sourceJsonObject.storage.base.attributes[i]; + if (curAttribute.default === "true") { + this._defaultAttributes.push(curAttribute); + } else if (curAttribute.default === "false") { + this._otherAttributes.push(curAttribute); + } else { + // cela ne doit pas arriver + } } - } - // stockage des attributs dans des tableaux pour le traitement des requêtes - this._defaultAttributesTable = new Array(); - this._defaultAttributesKeyTable = new Array(); - for (let i = 0; i < this._defaultAttributes.length; i++) { - this._defaultAttributesTable.push("'" + this._defaultAttributes[i].column + "'"); - this._defaultAttributesKeyTable.push(this._defaultAttributes[i].key); - } + // stockage des attributs dans des tableaux pour le traitement des requêtes + this._defaultAttributesTable = new Array(); + this._defaultAttributesKeyTable = new Array(); + for (let i = 0; i < this._defaultAttributes.length; i++) { + this._defaultAttributesTable.push("'" + this._defaultAttributes[i].column + "'"); + this._defaultAttributesKeyTable.push(this._defaultAttributes[i].key); + } - // stockage des attributs par défaut en une chaîne de caractères pour le traitement des requêtes - this._defaultAttributesString = this._defaultAttributesTable.join(","); + // stockage des attributs par défaut en une chaîne de caractères pour le traitement des requêtes + this._defaultAttributesString = this._defaultAttributesTable.join(","); - // Coûts disponibles - this._costs = {}; + } // Initialisation des coûts disponibles for (let i = 0; i < sourceJsonObject.costs.length; i++) { @@ -99,6 +104,8 @@ module.exports = class pgrSource extends Source { Object.defineProperty(this._costs[sourceJsonObject.costs[i].profile][sourceJsonObject.costs[i].costType], "rcostColumn", { value: sourceJsonObject.costs[i].rcostColumn, configurable: true, enumerable: true, writable: true }); } + // -- + } /** @@ -240,62 +247,72 @@ module.exports = class pgrSource extends Source { pgrRequest.coordinates = coordinatesTable; // --- waysAttributes - // attributes est déjà vide, on met les attributs par défaut - attributes = this._defaultAttributesString; + // attributes est déjà vide, on met les attributs par défaut s'il y en a + if (this._defaultAttributes.length !== 0) { + attributes = this._defaultAttributesString; + LOGGER.debug("default attributes: " + attributes); + } else { + LOGGER.debug("no default attributes available"); + } - LOGGER.debug("default attributes: " + attributes); - // on complète avec les attributs demandés - // TODO : refaire cette partie, et donc la manière dont l'info est stockée dans la classe - if (request.waysAttributes.length !== 0) { - let requestedAttributes = new Array(); + // S'il existe des attributs disponibles mais non par défaut, on complète avec les attributs demandés + if (this._otherAttributes.length !== 0 ) { + + // TODO : refaire cette partie, et donc la manière dont l'info est stockée dans la classe + if (request.waysAttributes.length !== 0) { + let requestedAttributes = new Array(); - for (let i = 0; i < request.waysAttributes.length; i++) { - // on récupère le nom de la colonne en fonction de l'id de l'attribut demandé - let isDefault = false; + for (let i = 0; i < request.waysAttributes.length; i++) { + // on récupère le nom de la colonne en fonction de l'id de l'attribut demandé + let isDefault = false; - for (let j = 0; j < this._defaultAttributes.length; j++) { - if (request.waysAttributes[i] === this._defaultAttributes[j].key) { - isDefault = true; - break; + for (let j = 0; j < this._defaultAttributes.length; j++) { + if (request.waysAttributes[i] === this._defaultAttributes[j].key) { + isDefault = true; + break; + } } - } - if (!isDefault) { - for (let j = 0; j < this._otherAttributes.length; j++) { - if (request.waysAttributes[i] === this._otherAttributes[j].key) { - requestedAttributes.push("'" + this._otherAttributes[j].column + "'"); - break; + if (!isDefault) { + for (let j = 0; j < this._otherAttributes.length; j++) { + if (request.waysAttributes[i] === this._otherAttributes[j].key) { + requestedAttributes.push("'" + this._otherAttributes[j].column + "'"); + break; + } } + } else { + // on passe au suivant } - } else { - // on passe au suivant + } - } + if (requestedAttributes.length !== 0) { - if (requestedAttributes.length !== 0) { + if (attributes !== "") { + attributes = attributes + "," + requestedAttributes.join(","); + } else { + attributes = requestedAttributes.join(","); + } + LOGGER.debug("final attributes: " + attributes); - if (attributes !== "") { - attributes = attributes + "," + requestedAttributes.join(","); } else { - attributes = requestedAttributes.join(","); + LOGGER.debug("no more attributes to add"); } - LOGGER.debug("final attributes: " + attributes); } else { - LOGGER.debug("no more attributes to add"); - } - - // --- waysAttributes - } else { + // on ne fait rien + LOGGER.debug("no more attributes were required"); - // on ne fait rien - LOGGER.debug("no more attributes were required"); + } + } else { + LOGGER.debug("no other attributes available"); } + // --- waysAttributes + if (request.constraints.length !== 0) { LOGGER.debug("constraints are asked"); @@ -595,7 +612,7 @@ module.exports = class pgrSource extends Source { LOGGER.debug("attributes management"); // On fait la liste des attributs par défaut - if (this._defaultAttributesKeyTable.length !== 0) { + if (this._defaultAttributes.length !== 0) { for (let i = 0; i < this._defaultAttributesKeyTable.length; i++) { finalAttributesKey.push(this._defaultAttributesKeyTable[i]); LOGGER.debug(this._defaultAttributesKeyTable[i] + " added"); @@ -606,34 +623,38 @@ module.exports = class pgrSource extends Source { } // On ajoute la liste des attributs demandés - if (routeRequest.waysAttributes.length !== 0) { - for (let i = 0; i < routeRequest.waysAttributes.length; i++) { - // on récupère le nom de la colonne en fonction de l'id de l'attribut demandé - let isDefault = false; - - for (let j = 0; j < this._defaultAttributes.length; j++) { - if (routeRequest.waysAttributes[i] === this._defaultAttributes[j].key) { - isDefault = true; - break; + if (this._otherAttributes.length !== 0) { + if (routeRequest.waysAttributes.length !== 0) { + for (let i = 0; i < routeRequest.waysAttributes.length; i++) { + // on récupère le nom de la colonne en fonction de l'id de l'attribut demandé + let isDefault = false; + + for (let j = 0; j < this._defaultAttributes.length; j++) { + if (routeRequest.waysAttributes[i] === this._defaultAttributes[j].key) { + isDefault = true; + break; + } } - } - if (!isDefault) { - for (let j = 0; j < this._otherAttributes.length; j++) { - if (routeRequest.waysAttributes[i] === this._otherAttributes[j].key) { - finalAttributesKey.push(this._otherAttributes[j].key); - LOGGER.debug(this._otherAttributes[j].key + " added"); - break; + if (!isDefault) { + for (let j = 0; j < this._otherAttributes.length; j++) { + if (routeRequest.waysAttributes[i] === this._otherAttributes[j].key) { + finalAttributesKey.push(this._otherAttributes[j].key); + LOGGER.debug(this._otherAttributes[j].key + " added"); + break; + } } + } else { + // on passe au suivant } - } else { - // on passe au suivant - } + } + } else { + // il n'y a aucun attribut demandé par l'utilisateur + LOGGER.debug("no attributes requested"); } } else { - // il n'y a aucun attribut demandé par l'utilisateur - LOGGER.debug("no attributes requested"); + LOGGER.debug("no other attributes"); } // TODO: Il n'y a qu'une route pour l'instant: à changer pour plusieurs routes diff --git a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature index 8aa32a5..798d9d8 100644 --- a/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature +++ b/test/functional/request/cucumber/features/req-simple-1.0.0-pgr.feature @@ -514,6 +514,122 @@ Scenario: [GET] Route sur l'API simple 1.0.0 avec une contrainte spécifique pgr And the response should have an header "content-type" with value "application/json" And the response should contain "Parameter 'constraints' is invalid" + Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec les waysAttributes par défaut + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.nom_1_droite" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec les waysAttributes par défaut + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.nom_1_droite" + + Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec mauvais waysAttributes + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | waysAttributes | test | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "error.message" with value "Parameter 'waysAttributes' is invalid" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec mauvais waysAttributes + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with table parameters for "waysAttributes": + | value | + | test | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "error.message" with value "Parameter 'waysAttributes' is invalid" + + + Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec un bon waysAttributes + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | waysAttributes | cleabs | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.cleabs" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec un bon waysAttributes + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with table parameters for "waysAttributes": + | value | + | cleabs | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.cleabs" + + Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec un bon waysAttributes doublé + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | waysAttributes | cleabs \| cleabs | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.cleabs" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec un bon waysAttributes doublé + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with table parameters for "waysAttributes": + | value | + | cleabs | + | cleabs | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.cleabs" + +Scenario Outline: [GET] Route sur l'API simple 1.0.0 avec un bon waysAttributes et un faux + Given an "GET" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with query parameters: + | key | value | + | waysAttributes | cleabs \| test | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.cleabs" + + Scenario Outline: [POST] Route sur l'API simple 1.0.0 avec un bon waysAttributes et un faux + Given an "POST" request on operation "route" in api "simple" "1.0.0" + And with default parameters for "route-pgr" + And with table parameters for "waysAttributes": + | value | + | cleabs | + | test | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain a complete and valid road + And the response should contain an attribute "portions.[0].steps.[0].attributes.cleabs" + + Scenario Outline: [] Isochrone sur l'API simple 1.0.0 Given an "" request on operation "isochrone" in api "simple" "1.0.0" And with default parameters for "isochrone" @@ -548,7 +664,7 @@ Scenario Outline: [] Isochrone sur l'API simple 1.0.0 And with default parameters for "isochrone" And with query parameters: | key | value | - | resource | bduni-idf-osrm-2 | + | resource | bduni-idf-pgr-2 | When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" From 8b3b2b0da8acd47544da62e23caf571fa2df3a9d Mon Sep 17 00:00:00 2001 From: lgrd Date: Wed, 29 Mar 2023 11:54:32 +0200 Subject: [PATCH 81/93] [feat] compute GET /configuration (#59) --- changelog.md | 1 + src/js/administrator/administrator.js | 11 ++++++++ src/js/apis/admin/1.0.0/index.js | 27 +++++++++++++++++++ .../cucumber/configurations/local-admin.json | 1 + .../cucumber/features/req-admin-1.0.0.feature | 24 +++++++++++++++++ 5 files changed, 64 insertions(+) diff --git a/changelog.md b/changelog.md index 2e00f61..414d2ef 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ ADDED: - Ajout de la route GET /admin/1.0.0/services dans l'API d'administration - Ajout de la route GET /admin/1.0.0/services/{service} dans l'API d'administration - Ajout de la route GET /admin/1.0.0/services/{service}/restart dans l'API d'administration + - Ajout de la route GET /admin/1.0.0/configuration dans l'API d'administration - Il est maintenant possible démarrer un administrateur sans services pré-configurés CHANGED: diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 49084f4..bcca61d 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -56,6 +56,17 @@ module.exports = class Administrator { } + /** + * + * @function + * @name get configuration + * @description Récupérer la configuration de l'administrateur + * + */ + get configuration () { + return this._configuration; + } + /** * * @function diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index 19fa757..f82474c 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -69,6 +69,33 @@ router.route("/health") }); +// Configuration +// Pour avoir ou changer la configuration de l'administrateur +router.route("/configuration") + + .get(async function(req, res, next) { + + LOGGER.debug("requete GET sur /admin/1.0.0/configuration?"); + LOGGER.debug(req.originalUrl); + + // On récupère l'instance d'Administrator pour répondre aux requêtes + let administrator = req.app.get("administrator"); + + try { + + // Envoie à l'administrateur et récupération de l'objet réponse + const configurationResponse = administrator.configuration; + LOGGER.debug(configurationResponse); + + res.set('content-type', 'application/json'); + res.status(200).json(configurationResponse); + + } catch (error) { + return next(error); + } + + }); + // Services // Pour avoir des informations sur les services router.route("/services") diff --git a/test/functional/request/cucumber/configurations/local-admin.json b/test/functional/request/cucumber/configurations/local-admin.json index aee46ec..1b4a3e6 100644 --- a/test/functional/request/cucumber/configurations/local-admin.json +++ b/test/functional/request/cucumber/configurations/local-admin.json @@ -7,6 +7,7 @@ "1.0.0": { "health": "/admin/1.0.0/health", "version": "/admin/1.0.0/version", + "configuration": "/admin/1.0.0/configuration", "services": "/admin/1.0.0/services", "services/": "/admin/1.0.0/services/", "services//restart": "/admin/1.0.0/services//restart" diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index 2ea6b63..092f0cf 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -66,6 +66,30 @@ Feature: Road2 with data Then the server should send a response with status 404 And the response should contain "Not found" + Scenario: [admin/1.0.0] Configuration de l'administrateur + Given an "GET" request on operation "configuration" in api "admin" "1.0.0" + When I send the request + Then the server should send a response with status 200 + And the response should contain "administration" + + Scenario: [admin/1.0.0] Configuration de l'administrateur avec un mauvais parametre + Given an "GET" request on operation "configuration" in api "admin" "1.0.0" + And with query parameters: + | key | value | + | test | other | + When I send the request + Then the server should send a response with status 200 + And the response should contain "administration" + + Scenario: [admin/1.0.0] Configuration de l'administrateur en POST ne marche pas + Given an "POST" request on operation "configuration" in api "admin" "1.0.0" + And with query parameters: + | key | value | + | test | other | + When I send the request + Then the server should send a response with status 404 + And the response should contain "Not found" + Scenario: [admin/1.0.0] Configurations des services Given an "GET" request on operation "services" in api "admin" "1.0.0" When I send the request From c984b5535611a77e8d17974d6f618be1d1991889 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Thu, 30 Mar 2023 14:47:39 +0200 Subject: [PATCH 82/93] feat(test web): add definition of user resource --- test/www/isochrone.html | 3 +++ test/www/js/isochrone.js | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/test/www/isochrone.html b/test/www/isochrone.html index 7d6e7f8..3a31dcd 100644 --- a/test/www/isochrone.html +++ b/test/www/isochrone.html @@ -42,6 +42,9 @@
+ + +

Mode de déplacement :

diff --git a/test/www/js/isochrone.js b/test/www/js/isochrone.js index 5983b27..10f825c 100644 --- a/test/www/js/isochrone.js +++ b/test/www/js/isochrone.js @@ -329,9 +329,17 @@ function loadUserParameter(request) { // Resource if (document.forms["iso-form"].elements["userResource"].value !== "") { request.finalResource = document.forms["iso-form"].elements["userResource"].value; + if (request.finalResource === "otherResource") { + if (document.forms["iso-form"].elements["userResourceValue"].value !== "") { + request.finalResource = document.forms["iso-form"].elements["userResourceValue"].value; + } else { + request.finalResource = defaultResource; + } + } } else { request.finalResource = defaultResource; } + // Profile if (document.forms["iso-form"].elements["userProfile"].value !== "") { request.finalProfile = document.forms["iso-form"].elements["userProfile"].value; From 4e180086ff83e546263ed5928a150781a924b437 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 5 Apr 2023 09:18:28 +0200 Subject: [PATCH 83/93] feat(projection): add route to get projection from service --- .../apis/administration/1.0.0/api.yaml | 58 ++++++++++++- src/js/administrator/administrator.js | 18 ++++ .../apis/admin/1.0.0/controller/controller.js | 64 ++++++++++++++ src/js/apis/admin/1.0.0/index.js | 44 ++++++++++ src/js/requests/projectionRequest.js | 83 +++++++++++++++++++ src/js/responses/projectionResponse.js | 56 +++++++++++++ src/js/service/service.js | 33 ++++++++ src/js/service/serviceManager.js | 2 +- .../cucumber/configurations/local-admin.json | 3 +- .../cucumber/features/req-admin-1.0.0.feature | 30 +++++++ .../requests/integrationProjectionRequest.js | 41 +++++++++ .../integrationProjectionResponse.js | 31 +++++++ 12 files changed, 460 insertions(+), 3 deletions(-) create mode 100644 src/js/requests/projectionRequest.js create mode 100644 src/js/responses/projectionResponse.js create mode 100644 test/integration/mocha/requests/integrationProjectionRequest.js create mode 100644 test/integration/mocha/responses/integrationProjectionResponse.js diff --git a/documentation/apis/administration/1.0.0/api.yaml b/documentation/apis/administration/1.0.0/api.yaml index 2e582a2..66df8c6 100644 --- a/documentation/apis/administration/1.0.0/api.yaml +++ b/documentation/apis/administration/1.0.0/api.yaml @@ -390,7 +390,55 @@ paths: application/json: schema: $ref: "#/components/schemas/errorResponse" - + + /services/{service}/projections/{projection}: + get: + tags: + - "Gestion des services" + - "Projection" + summary: "Récupération d'une projection supportée par un service." + description: | + Cette requête retourne une projection supportée par un service. + operationId: "get-service-projection" + parameters: + - name: "service" + description: "Id du service concerné" + in: "path" + required: true + schema: + type: "string" + - name: "projection" + description: "Id de la projection concernée" + in: "path" + required: true + schema: + type: "string" + responses: + 200: + description: "successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/projectionConfiguration" + 400: + description: "Invalid parameters" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 404: + description: "Not found" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + 500: + description: "Internal server error" + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + /services/{service}/resources: get: tags: @@ -953,6 +1001,14 @@ components: version: type: "string" example: "1.0.0" + + projectionConfiguration: + type: "object" + properties: + id: + type: "string" + example: "EPSG:4326" + required: true resourceConfiguration: type: "object" diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index bcca61d..4d87e19 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -475,6 +475,24 @@ module.exports = class Administrator { } + /** + * + * @function + * @name computeRequest + * @description Gestion de la requête pour une route d'administration sur un serveur + * @param {string} serviceId - Id du service selon l'administrateur + * @param {object} request - Instance fille de la classe Request + * + */ + + async computeRequest(serviceId, request) { + + LOGGER.info("computeRequest..."); + LOGGER.debug("Compute request pour le service : " + serviceId); + const response = await this._serviceManager.computeRequest(serviceId, request); + return response; + } + /** * * @function diff --git a/src/js/apis/admin/1.0.0/controller/controller.js b/src/js/apis/admin/1.0.0/controller/controller.js index 99dd146..4bb76fe 100644 --- a/src/js/apis/admin/1.0.0/controller/controller.js +++ b/src/js/apis/admin/1.0.0/controller/controller.js @@ -4,6 +4,7 @@ const errorManager = require('../../../../utils/errorManager'); const log4js = require('log4js'); const HealthRequest = require('../../../../requests/healthRequest'); const ServiceRequest = require('../../../../requests/serviceRequest'); +const ProjectionRequest = require('../../../../requests/projectionRequest'); var LOGGER = log4js.getLogger("CONTROLLER"); @@ -110,6 +111,69 @@ module.exports = { return request; + }, + + + /** + * + * @function + * @name checkProjectionParameters + * @description Vérification des paramètres d'une requête sur /services/{service}/projections/{projection} + * @param {object} parameters - ensemble des paramètres de la requête ExpressJS + * @return {ProjectionRequest} request - Instance de la classe ProjectionRequest + * + */ + + checkProjectionParameters: function(parameters) { + + LOGGER.debug("checkProjectionParameters()"); + + // Service + if (!parameters.service) { + throw errorManager.createError(" Parameter 'service' is invalid: there is no value", 400); + } + + if (parameters.service === "") { + throw errorManager.createError(" Parameter 'service' is invalid: value should not be empty", 400); + } + + // Projection + if (!parameters.projection) { + throw errorManager.createError(" Parameter 'projection' is invalid: there is no value", 400); + } + + if (parameters.projection === "") { + throw errorManager.createError(" Parameter 'projection' is invalid: value should not be empty", 400); + } + + // TODO : vérifier ici que le service existe (appel à une fonction de la classe administrator) + + const request = new ProjectionRequest(parameters.service, parameters.projection); + + return request; + + }, + /** + * + * @function + * @name writeProjectionResponse + * @description Ré-écriture de la réponse pour une requête sur /services//projections/ + * @param {object} projectionResponse - Instance de la classe ProjectionResponse + * @return {object} userResponse - Réponse envoyée à l'utilisateur + * + */ + + writeProjectionResponse: function(projectionResponse) { + + let userResponse = {}; + + LOGGER.debug("writeProjectionResponse()"); + + // On doit utiliser les attributs avec _ car les méthodes ne sont pas disponible dans le cadre d'une communication IPC + userResponse.id = projectionResponse._id; + + return userResponse; + } } \ No newline at end of file diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index f82474c..376c9a6 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -197,6 +197,50 @@ router.route("/services/:service/restart") }); +// Services/{service}/projections/{projection} +// Récupérer une projection supportée par un service +router.route("/services/:service/projections/:projection") + + .get(async function(req, res, next) { + + LOGGER.debug("requete GET sur /admin/1.0.0/services/:service/projections/:projection"); + LOGGER.debug(req.originalUrl); + + // On récupère l'instance d'Administrator pour répondre aux requêtes + let administrator = req.app.get("administrator"); + + // on récupère l'ensemble des paramètres de la requête + const parameters = req.params; + LOGGER.debug(parameters); + + try { + + // Vérification des paramètres de la requête + const projectionRequest = controller.checkProjectionParameters(parameters); + LOGGER.debug(projectionRequest); + + // Envoie à l'administrateur et récupération de l'objet réponse + const projectionResponse = await administrator.computeRequest(projectionRequest.service, projectionRequest); + LOGGER.debug(projectionResponse); + + // Vérification de l'id retourné (utilisation attribut car communication IPC) + if (projectionResponse._id == "") { + next(errorManager.createError("Unknown projection", 404)); + }else{ + // Formattage de la réponse + const userResponse = controller.writeProjectionResponse(projectionResponse); + LOGGER.debug(userResponse); + + res.set('content-type', 'application/json'); + res.status(200).json(userResponse); + } + + } catch (error) { + return next(error); + } + + }); + // Gestion des erreurs // Cette partie doit être placée après la définition des routes normales // --- diff --git a/src/js/requests/projectionRequest.js b/src/js/requests/projectionRequest.js new file mode 100644 index 0000000..139f7c4 --- /dev/null +++ b/src/js/requests/projectionRequest.js @@ -0,0 +1,83 @@ +'use strict'; + +const Request = require('./request'); + +/** +* +* @class +* @name projectionRequest +* @description Classe modélisant une requête sur une projection d'un service géré par l'administrateur. +* +*/ + +module.exports = class projectionRequest extends Request { + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe projectionRequest + * @param {string} serviceId - Id du service interrogé + * @param {string} projectionId - Id de la projection interrogée + * + */ + + constructor(serviceId, projectionId) { + + super("projection", "projectionRequest"); + + // Id du service d'après l'administrateur + this._service = serviceId; + + // Id de la projection + this._projection = projectionId; + + } + + /** + * + * @function + * @name get service + * @description Récupérer service de la requete + * + */ + get service() { + return this._service; + } + + /** + * + * @function + * @name set service + * @description Attribuer le service de la requete + * @param {string} id - Id du service + * + */ + set service(id) { + this._service = id; + } + + /** + * + * @function + * @name get projection + * @description Récupérer la projection demandée + * + */ + get projection() { + return this._projection; + } + + /** + * + * @function + * @name set projection + * @description Attribuer la projection de la requete + * @param {string} id - Id de la projection + * + */ + set projection(id) { + this._projection = id; + } + +} diff --git a/src/js/responses/projectionResponse.js b/src/js/responses/projectionResponse.js new file mode 100644 index 0000000..a4f369a --- /dev/null +++ b/src/js/responses/projectionResponse.js @@ -0,0 +1,56 @@ +'use strict'; + +const Response = require('./response'); + +/** +* +* @class +* @name projectionResponse +* @description Classe modélisant une réponse de description d'une projection +* +*/ + +module.exports = class projectionResponse extends Response { + + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe projectionResponse + * + */ + constructor() { + + // Type de la réponse + super("projectionResponse"); + + // Identifiant + this._id = ""; + + } + + /** + * + * @function + * @name get id + * @description Récupérer l'identifiant + * + */ + get id () { + return this._id; + } + + /** + * + * @function + * @name set id + * @description Attribuer l'identifiant + * @param {string} id - identifiant + * + */ + set id (id) { + this._id = id; + } + +} diff --git a/src/js/service/service.js b/src/js/service/service.js index 79ece38..f19078d 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -16,6 +16,8 @@ const LogManager = require('../utils/logManager'); const log4js = require('log4js'); const errorManager = require('../utils/errorManager'); const HealthResponse = require('../responses/healthResponse'); +const ProjectionResponse = require('../responses/projectionResponse'); +const projectionRequest = require('../requests/projectionRequest'); // Création du LOGGER const LOGGER = log4js.getLogger("SERVICE"); @@ -1045,6 +1047,8 @@ module.exports = class Service { // Le if est un choix modifiable. Pour le moment c'est ainsi car dans le cas du serviceProcess, on ne peut pas y échapper. if (request._type === "healthRequest") { return this.computeHealthRequest(request); + }else if (request._type === "projectionRequest") { + return this.computeProjectionRequest(request); } else { throw errorManager.createError("Unknown request type"); } @@ -1109,4 +1113,33 @@ module.exports = class Service { } + /** + * + * @function + * @name computeProjectionRequest + * @description Fonction utilisée pour connaitre une projection utilisée par un service + * @param {projectionRequest} projectionRequest - Instance de la classe projectionRequest + * + */ + + computeProjectionRequest(projectionRequest) { + + LOGGER.info("computeProjectionRequest..."); + + // On doit utiliser les attributs avec _ car les méthodes ne sont pas disponible dans le cadre d'une communication IPC + let projectionResponse = new ProjectionResponse(); + if ( !this._projectionManager.isProjectionLoaded(projectionRequest._projection)){ + projectionResponse.id = ""; + // Le lancement d'exception n'est pas possible car il n'y aura dans ce cas pas de réponse IPC envoyé avec l'uuid de la requete + // On envoie donc une réponse avec un id vide pour le traiter comme 404 dans l'administrateur + //throw errorManager.createError(`Can't find projection ${projectionRequest._projection}`, 404) + } + else{ + + projectionResponse.id = projectionRequest._projection; + } + + return projectionResponse; + } + } diff --git a/src/js/service/serviceManager.js b/src/js/service/serviceManager.js index c8e150d..6affa08 100644 --- a/src/js/service/serviceManager.js +++ b/src/js/service/serviceManager.js @@ -234,7 +234,7 @@ module.exports = class serviceManager { let administeredService = this._loadedServiceAdministeredCatalog[serviceId]; if (!administeredService) { LOGGER.error("Aucun service associé à cet ID: " + serviceId); - throw errorManager.createError("Unknown service : " + serviceId); + throw errorManager.createError("Unknown service : " + serviceId, 404); } // On envoit la requête et renvoit la réponse diff --git a/test/functional/request/cucumber/configurations/local-admin.json b/test/functional/request/cucumber/configurations/local-admin.json index 1b4a3e6..8b26c8f 100644 --- a/test/functional/request/cucumber/configurations/local-admin.json +++ b/test/functional/request/cucumber/configurations/local-admin.json @@ -10,7 +10,8 @@ "configuration": "/admin/1.0.0/configuration", "services": "/admin/1.0.0/services", "services/": "/admin/1.0.0/services/", - "services//restart": "/admin/1.0.0/services//restart" + "services//restart": "/admin/1.0.0/services//restart", + "services//projections/": "/admin/1.0.0/services//projections/" } } }, diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index 092f0cf..bc69e84 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -144,3 +144,33 @@ Feature: Road2 with data When I send the request Then the server should send a response with status 404 And the response should contain "Can't find service" + + Scenario: [admin/1.0.0] Projection valide du service "main" + Given an "GET" request on operation "services//projections/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | main | + | projection | EPSG:4326 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain "id" + + Scenario: [admin/1.0.0] Projection invalide du service "main" + Given an "GET" request on operation "services//projections/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | main | + | projection | ftii | + When I send the request + Then the server should send a response with status 404 + + Scenario: [admin/1.0.0] Projection invalide du service "main" + Given an "GET" request on operation "services//projections/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | Unknown | + | projection | Unknown | + When I send the request + Then the server should send a response with status 404 + \ No newline at end of file diff --git a/test/integration/mocha/requests/integrationProjectionRequest.js b/test/integration/mocha/requests/integrationProjectionRequest.js new file mode 100644 index 0000000..3ca344e --- /dev/null +++ b/test/integration/mocha/requests/integrationProjectionRequest.js @@ -0,0 +1,41 @@ +const assert = require('assert'); +const ProjectionRequest = require('../../../../src/js/requests/projectionRequest'); +const logManager = require('../logManager'); + +describe('Test de la classe ProjectionRequest', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let request = new ProjectionRequest("test", "EPSG:4326"); + + describe('Test du constructeur et des getters/setters', function() { + + it('Get type', function() { + assert.equal(request.type, "projectionRequest"); + }); + + it('Get service', function() { + assert.equal(request.service, "test"); + }); + + it('Set service', function() { + request.service = "main"; + assert.equal(request.service, "main"); + }); + + + it('Get projection', function() { + assert.equal(request.projection, "EPSG:4326"); + }); + + it('Set service', function() { + request.projection = "EPSG:2154"; + assert.equal(request.projection, "EPSG:2154"); + }); + + }); + +}); diff --git a/test/integration/mocha/responses/integrationProjectionResponse.js b/test/integration/mocha/responses/integrationProjectionResponse.js new file mode 100644 index 0000000..8808f84 --- /dev/null +++ b/test/integration/mocha/responses/integrationProjectionResponse.js @@ -0,0 +1,31 @@ +const assert = require('assert'); +const ProjectionResponse = require('../../../../src/js/responses/projectionResponse'); +const logManager = require('../logManager'); + +describe('Test de la classe ProjectionResponse', function() { + + before(function() { + // runs before all tests in this block + logManager.manageLogs(); + }); + + let response = new ProjectionResponse(); + + describe('Test du constructeur et des getters/setters', function() { + + it('Get type', function() { + assert.equal(response.type, "projectionResponse"); + }); + + it('Get id', function() { + assert.equal(response.id, ""); + }); + + it('Set id', function() { + response.id = "EPSG:4326"; + assert.equal(response.id, "EPSG:4326"); + }); + + }); + +}); From 0c48625ec3b57eb12b8a169ce81c2bb0217b1ef5 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Wed, 5 Apr 2023 14:57:49 +0200 Subject: [PATCH 84/93] feat(service): define _errorFlag in response to treat response as error. --- src/js/apis/admin/1.0.0/index.js | 17 ++++++----------- src/js/service/service.js | 15 +++++++++------ src/js/service/serviceProcess.js | 6 ++++++ .../cucumber/features/req-admin-1.0.0.feature | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/js/apis/admin/1.0.0/index.js b/src/js/apis/admin/1.0.0/index.js index 376c9a6..07a5cbe 100644 --- a/src/js/apis/admin/1.0.0/index.js +++ b/src/js/apis/admin/1.0.0/index.js @@ -222,18 +222,13 @@ router.route("/services/:service/projections/:projection") // Envoie à l'administrateur et récupération de l'objet réponse const projectionResponse = await administrator.computeRequest(projectionRequest.service, projectionRequest); LOGGER.debug(projectionResponse); + + // Formattage de la réponse + const userResponse = controller.writeProjectionResponse(projectionResponse); + LOGGER.debug(userResponse); - // Vérification de l'id retourné (utilisation attribut car communication IPC) - if (projectionResponse._id == "") { - next(errorManager.createError("Unknown projection", 404)); - }else{ - // Formattage de la réponse - const userResponse = controller.writeProjectionResponse(projectionResponse); - LOGGER.debug(userResponse); - - res.set('content-type', 'application/json'); - res.status(200).json(userResponse); - } + res.set('content-type', 'application/json'); + res.status(200).json(userResponse); } catch (error) { return next(error); diff --git a/src/js/service/service.js b/src/js/service/service.js index f19078d..7eb289d 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -992,6 +992,8 @@ module.exports = class Service { response = this.computeAdminRequest(request); } catch(error) { LOGGER.error("Erreur lors de l'exécution de la requête: " + error); + error._uuid = request._uuid; + error._errorFlag = true process.send(error); return false; } @@ -1000,7 +1002,10 @@ module.exports = class Service { response._uuid = request._uuid; process.send(response); } catch(error) { - LOGGER.error("Erreur lors de l'exécution de la requête: " + error); + // TODO : gestion plus fine du renvoi d'un message, il faudrait que l'administrateur vérifie l'état du canal + LOGGER.error("Erreur lors de l'envoi de la réponse à la requête: " + error); + error._uuid = request._uuid; + error._errorFlag = true process.send(error); return false; } @@ -1119,20 +1124,18 @@ module.exports = class Service { * @name computeProjectionRequest * @description Fonction utilisée pour connaitre une projection utilisée par un service * @param {projectionRequest} projectionRequest - Instance de la classe projectionRequest + * @returns {projectionResponse} response - Instance de la classe projectionResponse * */ - computeProjectionRequest(projectionRequest) { + computeProjectionRequest(projectionRequest){ LOGGER.info("computeProjectionRequest..."); // On doit utiliser les attributs avec _ car les méthodes ne sont pas disponible dans le cadre d'une communication IPC let projectionResponse = new ProjectionResponse(); if ( !this._projectionManager.isProjectionLoaded(projectionRequest._projection)){ - projectionResponse.id = ""; - // Le lancement d'exception n'est pas possible car il n'y aura dans ce cas pas de réponse IPC envoyé avec l'uuid de la requete - // On envoie donc une réponse avec un id vide pour le traiter comme 404 dans l'administrateur - //throw errorManager.createError(`Can't find projection ${projectionRequest._projection}`, 404) + throw errorManager.createError(`Can't find projection ${projectionRequest._projection}`, 404); } else{ diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js index 8f67b41..b0b8645 100644 --- a/src/js/service/serviceProcess.js +++ b/src/js/service/serviceProcess.js @@ -203,6 +203,12 @@ module.exports = class ServiceProcess extends ServiceAdministered { // On attend la réponse et on la renvoit let response = await this.waitResponse(request._uuid); + + // On vérifie s'il s'agit d'une erreur + if (response._errorFlag) { + // TODO : voir si on fait une fonction pour ajouter la récupération de la stack + throw errorManager.createError(response.error, response.status) + } return response; diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index bc69e84..62b0b5c 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -161,7 +161,7 @@ Feature: Road2 with data And with path parameters: | key | value | | service | main | - | projection | ftii | + | projection | unknown | When I send the request Then the server should send a response with status 404 From a6e4ed7193ecb7ed31a837fba2f8b96f65b31406 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Thu, 6 Apr 2023 09:04:15 +0200 Subject: [PATCH 85/93] feat(projection): return 400 if empty parameter --- src/js/administrator/administrator.js | 1 + .../apis/admin/1.0.0/controller/controller.js | 6 +++--- .../cucumber/features/req-admin-1.0.0.feature | 21 ++++++++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/js/administrator/administrator.js b/src/js/administrator/administrator.js index 4d87e19..4fb10c8 100644 --- a/src/js/administrator/administrator.js +++ b/src/js/administrator/administrator.js @@ -482,6 +482,7 @@ module.exports = class Administrator { * @description Gestion de la requête pour une route d'administration sur un serveur * @param {string} serviceId - Id du service selon l'administrateur * @param {object} request - Instance fille de la classe Request + * @return {object} responses - Json contenant la réponse du service à la requête * */ diff --git a/src/js/apis/admin/1.0.0/controller/controller.js b/src/js/apis/admin/1.0.0/controller/controller.js index 4bb76fe..022a4c8 100644 --- a/src/js/apis/admin/1.0.0/controller/controller.js +++ b/src/js/apis/admin/1.0.0/controller/controller.js @@ -101,7 +101,7 @@ module.exports = { throw errorManager.createError(" Parameter 'service' is invalid: there is no value", 400); } - if (parameters.service === "") { + if (parameters.service.trim() === "") { throw errorManager.createError(" Parameter 'service' is invalid: value should not be empty", 400); } @@ -133,7 +133,7 @@ module.exports = { throw errorManager.createError(" Parameter 'service' is invalid: there is no value", 400); } - if (parameters.service === "") { + if (parameters.service.trim() === "") { throw errorManager.createError(" Parameter 'service' is invalid: value should not be empty", 400); } @@ -142,7 +142,7 @@ module.exports = { throw errorManager.createError(" Parameter 'projection' is invalid: there is no value", 400); } - if (parameters.projection === "") { + if (parameters.projection.trim() === "") { throw errorManager.createError(" Parameter 'projection' is invalid: value should not be empty", 400); } diff --git a/test/functional/request/cucumber/features/req-admin-1.0.0.feature b/test/functional/request/cucumber/features/req-admin-1.0.0.feature index 62b0b5c..efc33d6 100644 --- a/test/functional/request/cucumber/features/req-admin-1.0.0.feature +++ b/test/functional/request/cucumber/features/req-admin-1.0.0.feature @@ -172,5 +172,24 @@ Feature: Road2 with data | service | Unknown | | projection | Unknown | When I send the request - Then the server should send a response with status 404 + Then the server should send a response with status 404 + + Scenario: [admin/1.0.0] Projection sur service non défini + Given an "GET" request on operation "services//projections/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | %20 | + | projection | EPSG:4326 | + When I send the request + Then the server should send a response with status 400 + + + Scenario: [admin/1.0.0] Projection non définie sur service "main" + Given an "GET" request on operation "services//projections/" in api "admin" "1.0.0" + And with path parameters: + | key | value | + | service | main | + | projection | %20 | + When I send the request + Then the server should send a response with status 400 \ No newline at end of file From 66572645a9e299cf37059acc73dc3f53a8d17102 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Thu, 6 Apr 2023 09:04:30 +0200 Subject: [PATCH 86/93] feat(projection): update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 414d2ef..935a67a 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ ADDED: - Ajout de la route GET /admin/1.0.0/services dans l'API d'administration - Ajout de la route GET /admin/1.0.0/services/{service} dans l'API d'administration - Ajout de la route GET /admin/1.0.0/services/{service}/restart dans l'API d'administration + - Ajout de la route GET /admin/1.0.0/services/{service}/projections/{projection} dans l'API d'administration - Ajout de la route GET /admin/1.0.0/configuration dans l'API d'administration - Il est maintenant possible démarrer un administrateur sans services pré-configurés From 68df61750c06b41cd864dab47088e130593dcc5c Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 6 Apr 2023 11:57:59 +0200 Subject: [PATCH 87/93] [feat] add error message when needed --- src/js/service/service.js | 22 +++++++++++++++------- src/js/service/serviceProcess.js | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/js/service/service.js b/src/js/service/service.js index 7eb289d..f397f58 100644 --- a/src/js/service/service.js +++ b/src/js/service/service.js @@ -991,9 +991,15 @@ module.exports = class Service { try { response = this.computeAdminRequest(request); } catch(error) { - LOGGER.error("Erreur lors de l'exécution de la requête: " + error); + // C'est un cas particulier : on veut retrouver l'usage des controller d'APIs + // => comme c'est une requête, on fait du throw en cas d'erreur et c'est normal + // ce n'est pas une vraie erreur. Cependant, on veut renvoyer l'erreur au parent + // donc on copie le message + LOGGER.info("Erreur lors de l'exécution de la requête: " + error.message); error._uuid = request._uuid; - error._errorFlag = true + error._errorFlag = true; + error._message = error.message; + error._stack = error.stack; process.send(error); return false; } @@ -1005,7 +1011,9 @@ module.exports = class Service { // TODO : gestion plus fine du renvoi d'un message, il faudrait que l'administrateur vérifie l'état du canal LOGGER.error("Erreur lors de l'envoi de la réponse à la requête: " + error); error._uuid = request._uuid; - error._errorFlag = true + error._errorFlag = true; + error._message = error.message; + error._stack = error.stack; process.send(error); return false; } @@ -1134,15 +1142,15 @@ module.exports = class Service { // On doit utiliser les attributs avec _ car les méthodes ne sont pas disponible dans le cadre d'une communication IPC let projectionResponse = new ProjectionResponse(); - if ( !this._projectionManager.isProjectionLoaded(projectionRequest._projection)){ - throw errorManager.createError(`Can't find projection ${projectionRequest._projection}`, 404); - } - else{ + if (!this._projectionManager.isProjectionLoaded(projectionRequest._projection)) { + throw errorManager.createError(`Can't find projection ${projectionRequest._projection}`, 404); + } else { projectionResponse.id = projectionRequest._projection; } return projectionResponse; + } } diff --git a/src/js/service/serviceProcess.js b/src/js/service/serviceProcess.js index b0b8645..f513d0d 100644 --- a/src/js/service/serviceProcess.js +++ b/src/js/service/serviceProcess.js @@ -206,8 +206,8 @@ module.exports = class ServiceProcess extends ServiceAdministered { // On vérifie s'il s'agit d'une erreur if (response._errorFlag) { - // TODO : voir si on fait une fonction pour ajouter la récupération de la stack - throw errorManager.createError(response.error, response.status) + // TODO : voir ce qu'on fait de la stack dispo dans response._stack + throw errorManager.createError(response._message, response.status); } return response; From 828c04585c86355a8572fefd85ddbff7a9dcc520 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Thu, 6 Apr 2023 13:02:15 +0200 Subject: [PATCH 88/93] fix(ci) use x.y.z versionning --- .github/workflows/docker_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 2b6f29c..64dc5ff 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -11,7 +11,7 @@ on: push: branches: [ "develop" ] # Publish semver tags as releases. - tags: [ 'v*.*.*' ] + tags: [ '*.*.*' ] pull_request: branches: [ "develop" ] From bc86ae495846cee42590e370ec35e54d6dbee9a9 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Thu, 6 Apr 2023 14:50:32 +0200 Subject: [PATCH 89/93] feat(version): update version to 2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 677658b..b7c2706 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "2.1.0-DEVELOP", + "version": "2.1.0", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", From f76ae90ad184dbe783ac74821f2131c3eff03af2 Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Thu, 6 Apr 2023 14:51:58 +0200 Subject: [PATCH 90/93] feat(version): update to 2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 677658b..b7c2706 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "2.1.0-DEVELOP", + "version": "2.1.0", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", From 863678a3c97f8d2f1399256ae9a7e95bcb32f09a Mon Sep 17 00:00:00 2001 From: jmkerloch Date: Fri, 7 Apr 2023 15:32:17 +0200 Subject: [PATCH 91/93] feat(demo): add service url in isochrone test --- test/www/isochrone.html | 1 + test/www/js/isochrone.js | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/www/isochrone.html b/test/www/isochrone.html index 3a31dcd..abab19e 100644 --- a/test/www/isochrone.html +++ b/test/www/isochrone.html @@ -31,6 +31,7 @@
+

Paramètres de l'isochrone

:
diff --git a/test/www/js/isochrone.js b/test/www/js/isochrone.js index 10f825c..d29d91d 100644 --- a/test/www/js/isochrone.js +++ b/test/www/js/isochrone.js @@ -220,7 +220,7 @@ function computeIso() { // -- // ---- Requete envoyée au nouveau service - let requestStr = road2Url + + let requestStr = request.finalUrl + "resource=" + request.finalResource + "&profile=" + request.finalProfile + "&costType=" + request.finalCostType + @@ -325,6 +325,12 @@ function computeOtherRoad(request) { function loadUserParameter(request) { let constraintObject = {}; + // Url + if (document.forms["iso-form"].elements["userUrl"].value !== "") { + request.finalUrl = document.forms["route-form"].elements["userUrl"].value; + } else { + request.finalUrl = road2Url; + } // Resource if (document.forms["iso-form"].elements["userResource"].value !== "") { From 2c754b1e3059f197f2a5fcce785a9683fdae485e Mon Sep 17 00:00:00 2001 From: lgrd Date: Wed, 3 May 2023 15:26:19 +0200 Subject: [PATCH 92/93] [ci] move the doc and docker part on master (#75) * [ci] move docker and doc deployment on master * [doc] update doc for developers --- .github/workflows/docker_publish.yml | 6 ++---- .github/workflows/documentation.yml | 19 ++++++++++++------- changelog.md | 9 +++++++++ documentation/conf.py | 2 +- documentation/developers/functionnalities.md | 13 ++++++++++++- documentation/developers/version.md | 11 ++++++++++- package.json | 2 +- 7 files changed, 47 insertions(+), 15 deletions(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 64dc5ff..1792009 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -6,14 +6,12 @@ name: Docker GitHub Container Registry # documentation. on: - #schedule: - # - cron: '40 18 * * *' push: - branches: [ "develop" ] + branches: [ "master" ] # Publish semver tags as releases. tags: [ '*.*.*' ] pull_request: - branches: [ "develop" ] + branches: [ "master", "develop" ] env: # Use docker.io for Docker Hub if empty diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 75e02b2..6c0a641 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -4,7 +4,9 @@ name: Deploy static content to Pages on: # Runs on pushes targeting the default branch push: - branches: ["develop"] + branches: ["master"] + pull_request: + branches: [ "master", "develop" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -24,14 +26,9 @@ env: PYTHON_VERSION: "3.10" jobs: - # Single deploy job since we're just deploying - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + build: runs-on: ubuntu-latest steps: - - name: Checkout uses: actions/checkout@v3 @@ -58,6 +55,14 @@ jobs: with: path: 'documentation/_build/html' + deploy: + if: github.event_name != 'pull_request' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v1 diff --git a/changelog.md b/changelog.md index 935a67a..26d7546 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # CHANGELOG +## 2.1.1 + +CHANGED: + - reference de la doc à la branche master + - modification de la ci github pour prendre en compte la branche master + +FIXED: +- correction pour éviter de publier github pages sur un tag + ## 2.1.0 ADDED: diff --git a/documentation/conf.py b/documentation/conf.py index ab89f09..b32e40f 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -102,7 +102,7 @@ "author": author, "date_update": datetime.now().strftime("%d %B %Y"), "description": description, - "repo_branch": "develop", + "repo_branch": "master", "repo_url": uri_repository, "title": project, "version": version, diff --git a/documentation/developers/functionnalities.md b/documentation/developers/functionnalities.md index 9539dfa..b731dc8 100644 --- a/documentation/developers/functionnalities.md +++ b/documentation/developers/functionnalities.md @@ -129,8 +129,19 @@ Fonctionnalité explicite. ### Obtenir l'état de santé du serveur via l'API -Fonctionnalité en cours d'implémentation. L'objectif est de récupérer l'état de chaque [source](./concepts.md) du serveur et d'en faire un compte-rendu. +L'objectif est de récupérer l'état de chaque [source](./concepts.md) de chaque service et d'en faire un compte-rendu. +### Obtenir la configuration de l'administrateur + +### Obtenir la configuration des services gérés par un administrateur + +### Obtenir la configuration d'un service via son id + +### Redémarrer un service via son id + +### Obtenir la liste des projections gérés par un service + +### Obtenir la présence d'une projection via son id ## Groupe de fonctionnalités 5 : Fonctionnalités spécifiques à OSRM diff --git a/documentation/developers/version.md b/documentation/developers/version.md index f114c30..85b0380 100644 --- a/documentation/developers/version.md +++ b/documentation/developers/version.md @@ -14,7 +14,14 @@ Route Graph Generator et PGRouting Procedures sont indépendants en tant que pro Sur ces trois projets ont une branche `master` et `develop`. La première permet de gérer les versions mises en production. La seconde permet de réaliser les développements. -On veillera à partir de `develop` et de créer une branche du type `feature-*` pour réaliser de nouvelles fonctionnalités. +On veillera à partir de `develop` et de créer une branche du type +- `feat/*` pour réaliser de nouvelles fonctionnalités, +- `fix/*` pour effectuer une correction sur le code source, +- `docker/*` pour modifier la partie docker uniquement, +- `test/*` pour modifier uniquement les tests, +- `ci/*` pour modifier la CI Github + +Pour fusionner une branche avec `develop`, on veillera à avoir fait un rebase de develop sur cette branche. Et sur la méthode de merge, on fera un squash. Ainsi, la branche `develop` aura un commit par fonctionnalité, correction, etc... ## Versions et tags @@ -32,6 +39,8 @@ On veillera à tagger les commits de chaque projet avec les bonnes versions. Et - On doit être capable d'identifier, par les tags, les versions du code utilisées en production. - On doit pouvoir faire fonctionner tous les projets ensemble à partir des tags sur `master` et `develop`. +Lorsque l'on fusionnera `develop` sur `master`, on veillera à ne pas faire de squash afin de faciliter les futurs fusions (comme cela est préconisé par [github](https://docs.github.com/fr/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squashing-and-merging-a-long-running-branch)). + ### PGRouting Procedures et Route Graph Generator Il est conseillé de commencer par gérer les versions de ces deux là. *Ce qui suit décrit le processus de mise à jour des projets, mais sans passer par les submodules de GIT*. Si on souhaite passer par les submodules, on pourra se référer à la documentation [proposée par GIT](https://git-scm.com/book/fr/v2/Utilitaires-Git-Sous-modules). diff --git a/package.json b/package.json index b7c2706..35068f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "2.1.0", + "version": "2.1.1", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", From a2502bcceac9da08f3e1f994d164d4092e653b27 Mon Sep 17 00:00:00 2001 From: lgrd Date: Thu, 4 May 2023 15:56:52 +0200 Subject: [PATCH 93/93] [submodule] update to last master commits (#76) --- .gitmodules | 4 ++-- docker/motors/pgrouting-procedures | 2 +- docker/route-graph-generator | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2d208c3..3174efe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ [submodule "docker/route-graph-generator"] path = docker/route-graph-generator url = https://github.com/IGNF/route-graph-generator.git - branch = develop + branch = master [submodule "docker/motors/pgrouting-procedures"] path = docker/motors/pgrouting-procedures url = https://github.com/IGNF/pgrouting-procedures.git - branch = develop + branch = master diff --git a/docker/motors/pgrouting-procedures b/docker/motors/pgrouting-procedures index 261ca48..724deaa 160000 --- a/docker/motors/pgrouting-procedures +++ b/docker/motors/pgrouting-procedures @@ -1 +1 @@ -Subproject commit 261ca48c5f13a663dca6e4971e5775e4c9487c47 +Subproject commit 724deaaba5bc71789ae29df83c70696f089c9c32 diff --git a/docker/route-graph-generator b/docker/route-graph-generator index d197411..610c430 160000 --- a/docker/route-graph-generator +++ b/docker/route-graph-generator @@ -1 +1 @@ -Subproject commit d19741120b915d1cf034e099fe5ccad1cbb63e0c +Subproject commit 610c430c1f11e3090a082a77c464bf6e129f4e8e