From ab1e66701d13c4e13118e03d93ad811dd12bd5c3 Mon Sep 17 00:00:00 2001
From: gcornut <guillaume.cornut@inra.fr>
Date: Fri, 8 Feb 2019 17:11:57 +0100
Subject: [PATCH 1/6] chore: Configure continuous delivery in `.gitlab-ci.yml`.
 Change default context path and port.

---
 .gitlab-ci.yml                             | 141 ++++++++++++++++++++-
 README.md                                  |  22 +++-
 backend/src/main/resources/application.yml |   4 +-
 frontend/angular.json                      |   2 +-
 frontend/proxy.conf.js                     |   6 +-
 5 files changed, 159 insertions(+), 16 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f7ac1e16..b7fb7910 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,3 +1,10 @@
+---
+stages:
+  - test
+  - build
+  - staging
+  - production
+
 image: urgi/docker-browsers:latest
 
 # Disable the Gradle daemon for Continuous Integration servers as correctness
@@ -7,14 +14,16 @@ image: urgi/docker-browsers:latest
 variables:
   GRADLE_OPTS: "-Dorg.gradle.daemon=false"
   GRADLE_USER_HOME: $CI_PROJECT_DIR/.gradle
+  APP_NAME: gpds
+  JAR_PATH: "backend/build/libs/${APP_NAME}.jar"
 
 # Gradle cache for all jobs
 cache:
   key: "$CI_COMMIT_REF_NAME"
   paths:
-  - ".gradle"
-  - "frontend/.gradle/"
-  - "frontend/node_modules/"
+    - ".gradle"
+    - "frontend/.gradle/"
+    - "frontend/node_modules/"
 
 # TESTS
 
@@ -28,7 +37,7 @@ test-frontend:
   artifacts:
     reports:
       junit:
-      - "./frontend/karma-junit-tests-report/TEST*.xml"
+        - "./frontend/karma-junit-tests-report/TEST*.xml"
 
 test-backend:
   stage: test
@@ -52,3 +61,127 @@ test-backend:
       junit:
         - "./backend/build/test-results/test/TEST-*.xml"
 
+# BUILD
+
+build:
+  stage: build
+  script:
+    - ./gradlew assemble
+  artifacts:
+    paths:
+      - "$JAR_PATH"
+    expire_in: 1 week
+  only:
+    changes:
+      - backend/src/**/*
+      - frontend/**/*
+
+# DEPLOY
+
+.deploy-to-vm: &deploy_to_vm
+  # Hidden job which serves as template for executed jobs below.
+  # See https://docs.gitlab.com/ee/ci/yaml/#anchors
+  retry: 2
+  ## SSH initialization
+  before_script:
+    - eval $(ssh-agent -s)
+    - ssh-add <(echo "${SSH_PRIVATE_KEY}")
+    - ssh -o StrictHostKeyChecking=no root@${SERVER_IP} 'echo "Successfully connected on $(hostname)"'
+  script:
+    # Copy jar and data (cleaning them before) to the server
+    - "scp ./$JAR_PATH root@${SERVER_IP}:/opt/${APP_NAME}/${APP_NAME}-${ENV}.jar"
+    # Restarting service with the updated jar and the according Spring profiles enabled
+    - "ssh root@${SERVER_IP} \"systemctl restart ${APP_NAME}-${ENV}\""
+    - eval $(ssh-agent -k)
+    - "echo \"Deploy done. Application should be available at: http://${SERVER_IP}:${APP_PORT}/${CONTEXT_PATH} \""
+  only:
+    changes:
+      - backend/src/**/*
+      - frontend/**/*
+
+
+deploy-to-prod:
+  stage: production
+  variables:
+    ENV: prod
+    APP_PORT: ${PROD_GPDS_PORT}
+    CONTEXT_PATH: ${PROD_GPDS_CONTEXT_PATH}
+  <<: *deploy_to_vm
+  only:
+    refs:
+      - master
+  when: manual
+  allow_failure: false
+
+
+deploy-to-beta:
+  stage: staging
+  variables:
+    ENV: beta
+    APP_PORT: ${BETA_GPDS_PORT}
+    CONTEXT_PATH: ${BETA_GPDS_CONTEXT_PATH}
+  <<: *deploy_to_vm
+  only:
+    refs:
+      - branches
+  except:
+    refs:
+      - master
+  allow_failure: false
+
+
+deploy-to-staging:
+  stage: staging
+  variables:
+    ENV: staging
+    APP_PORT: ${STAGING_GPDS_PORT}
+    CONTEXT_PATH: ${STAGING_GPDS_CONTEXT_PATH}
+  <<: *deploy_to_vm
+  only:
+    refs:
+      - master
+
+
+deploy-to-private-prod:
+  stage: production
+  variables:
+    ENV: private-prod
+    APP_PORT: ${PRIVATE_PROD_GPDS_PORT}
+    CONTEXT_PATH: ${PRIVATE_PROD_GPDS_CONTEXT_PATH}
+  <<: *deploy_to_vm
+  only:
+    refs:
+      - master
+  when: manual
+  allow_failure: false
+
+
+deploy-to-private-int:
+  stage: production
+  variables:
+    ENV: private-int
+    APP_PORT: ${PRIVATE_INT_GPDS_PORT}
+    CONTEXT_PATH: ${PRIVATE_INT_GPDS_CONTEXT_PATH}
+  <<: *deploy_to_vm
+  only:
+    refs:
+      - master
+  when: manual
+  allow_failure: false
+
+
+deploy-to-private-beta:
+  stage: staging
+  variables:
+    ENV: private-beta
+    APP_PORT: ${PRIVATE_BETA_GPDS_PORT}
+    CONTEXT_PATH: ${PRIVATE_BETA_GPDS_CONTEXT_PATH}
+  <<: *deploy_to_vm
+  only:
+    refs:
+      - branches
+  except:
+    refs:
+      - master
+  when: manual
+  allow_failure: false
diff --git a/README.md b/README.md
index 2204ee66..246c9c0b 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ Otherwise, for the complete server (backend APIs + frontend interface), you can
 ./gradlew assemble && java -jar backend/build/libs/gpds.jar
 ```
 
-The server should then be accessible at http://localhost:8080/gnpis-core
+The server should then be accessible at http://localhost:8060/gnpis/gpds
 
 ## Run frontend development server
 
@@ -110,18 +110,28 @@ If such a configuration is not found, it will then fallback to the local `applic
 To avoid running the Spring Cloud config server every time when developing the application,
 all the properties are still available in `application.yml` even if they are configured on the remote Spring Cloud server as well.
 
-> **TODO**: Create a sping cloud configuration git repository and update the following section:
-
-> If you want to use the Spring Cloud config app locally,
+If you want to use the Spring Cloud config app locally,
 see https://forgemia.inra.fr/urgi-is/data-discovery-config
 
-> The configuration is currently only read on startup,
+The configuration is currently only read on startup,
 meaning the application has to be reboot if the configuration is changed on the Spring Cloud server.
 For a dynamic reload without restarting the application,
 see http://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#refresh-scope
 to check what has to be changed.
 
-> In case of testing configuration from the config server, one may use a dedicated branch on `data-discovery-config` project
+In case of testing configuration from the config server, one may use a dedicated branch on `data-discovery-config` project
 and append the `--spring.cloud.config.label=<branch name to test>` parameter when starting the application's executable jar.
 More info on how pass a parameter to a Spring Boot app:
 https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config
+
+### Application port & context path
+
+|                | GPDS GnpIS               |
+| :------------: | :----------------------  |
+| dev            | :8060/gnpis/gpds         |
+| beta           | :8061/gnpis/gpds-beta    |
+| staging        | :8062/staging/gnpis/gpds |
+| prod           | :8063/gnpis/gpds         |
+| private prod   | :8064/private/gnpis/gpds |
+| private beta   | :8065/beta/gnpis/gpds    |
+| private int    | :8066/int/gnpis/gpds     |
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index c84caa56..a1b851e8 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -52,7 +52,7 @@ gpds:
   security-user-group-ws-token:
 
 server:
-  port: 8080
+  port: 8060
   servlet:
-    context-path: /gnpis-core
+    context-path: /gnpis/gpds
 
diff --git a/frontend/angular.json b/frontend/angular.json
index cd374be5..bd667ae3 100644
--- a/frontend/angular.json
+++ b/frontend/angular.json
@@ -42,7 +42,7 @@
               "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
             ],
             "scripts": [],
-            "baseHref": "/gnpis-core/"
+            "baseHref": "/gnpis/gpds/"
           },
           "configurations": {
             "production": {
diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js
index f0390d2a..d5aee375 100644
--- a/frontend/proxy.conf.js
+++ b/frontend/proxy.conf.js
@@ -1,10 +1,10 @@
 const PROXY_CONFIG = [
     {
         context: [
-            "/gnpis-core/brapi",
-            "/gnpis-core/gnpis",
+            "/gnpis/gpds/brapi",
+            "/gnpis/gpds/gnpis",
         ],
-        target: "http://localhost:8080",
+        target: "http://localhost:8060",
         secure: false
     }
 ];
-- 
GitLab


From 77bb54bb41a7d1f9f2656ef685c53ef1ca26c907 Mon Sep 17 00:00:00 2001
From: gcornut <guillaume.cornut@inra.fr>
Date: Mon, 11 Feb 2019 14:32:40 +0100
Subject: [PATCH 2/6] fix: Fix CI/CD cache for JAR file.

---
 .gitlab-ci.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b7fb7910..6597f501 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,6 +24,7 @@ cache:
     - ".gradle"
     - "frontend/.gradle/"
     - "frontend/node_modules/"
+    - "$JAR_PATH"
 
 # TESTS
 
@@ -73,6 +74,7 @@ build:
     expire_in: 1 week
   only:
     changes:
+      - .gitlab-ci.yml
       - backend/src/**/*
       - frontend/**/*
 
@@ -96,6 +98,7 @@ build:
     - "echo \"Deploy done. Application should be available at: http://${SERVER_IP}:${APP_PORT}/${CONTEXT_PATH} \""
   only:
     changes:
+      - .gitlab-ci.yml
       - backend/src/**/*
       - frontend/**/*
 
-- 
GitLab


From 2e1a9ab2b5c64ceab9a24c2a4c94e071b222e109 Mon Sep 17 00:00:00 2001
From: gcornut <guillaume.cornut@inra.fr>
Date: Mon, 11 Feb 2019 16:10:28 +0100
Subject: [PATCH 3/6] fix: Remove unnecessary ES cluster name. Adjust
 verbosity.

---
 .gitlab-ci.yml                                              | 2 +-
 .../java/fr/inra/urgi/gpds/config/ElasticSearchConfig.java  | 3 ---
 backend/src/main/resources/application.yml                  | 6 +++---
 backend/src/test/resources/test.properties                  | 1 -
 docker-compose.yml                                          | 1 -
 5 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6597f501..fc8593b4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -56,7 +56,7 @@ test-backend:
       # so there will be no bootstrap checks that would fail on CI
       # especially the error regarding
       # `max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]`
-      command: ["bin/elasticsearch", "-Ediscovery.type=single-node", "-Ecluster.name=es-gpds"]
+      command: ["bin/elasticsearch", "-Ediscovery.type=single-node"]
   artifacts:
     reports:
       junit:
diff --git a/backend/src/main/java/fr/inra/urgi/gpds/config/ElasticSearchConfig.java b/backend/src/main/java/fr/inra/urgi/gpds/config/ElasticSearchConfig.java
index 969bd3e3..90d5dbbc 100644
--- a/backend/src/main/java/fr/inra/urgi/gpds/config/ElasticSearchConfig.java
+++ b/backend/src/main/java/fr/inra/urgi/gpds/config/ElasticSearchConfig.java
@@ -13,9 +13,6 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class ElasticSearchConfig {
 
-    @Value("${data.elasticsearch.cluster-name}")
-    private String esClusterName;
-
     @Value("${data.elasticsearch.host}")
     private String esHost;
 
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index a1b851e8..02cfa4f3 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -1,8 +1,7 @@
 data:
   elasticsearch:
-      cluster-name: es-dev
-      host: 127.0.0.1
-      port: 9200
+    host: 127.0.0.1
+    port: 9200
 
 management:
   endpoint:
@@ -27,6 +26,7 @@ logging.level:
   org.springframework:
     boot.web.embedded.tomcat.TomcatWebServer: INFO
     web.client.RestTemplate: DEBUG
+  fr.inra: DEBUG
 
 ---
 spring:
diff --git a/backend/src/test/resources/test.properties b/backend/src/test/resources/test.properties
index 6d5d572e..a093f5b0 100644
--- a/backend/src/test/resources/test.properties
+++ b/backend/src/test/resources/test.properties
@@ -1,4 +1,3 @@
-spring.data.elasticsearch.cluster.name=es-gpds
 spring.data.elasticsearch.host=localhost
 spring.data.elasticsearch.port=9200
 
diff --git a/docker-compose.yml b/docker-compose.yml
index b1fb79fc..4cc0e3ef 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -4,7 +4,6 @@ services:
     image: docker.elastic.co/elasticsearch/elasticsearch:6.5.4
     container_name: elasticsearch
     environment:
-      - cluster.name=es-dev
       - discovery.type=single-node
     ports:
       - 9200:9200
-- 
GitLab


From 74d039f774c01e52ac9ca5647db12e1a60b2f344 Mon Sep 17 00:00:00 2001
From: gcornut <guillaume.cornut@inra.fr>
Date: Thu, 14 Feb 2019 16:15:34 +0100
Subject: [PATCH 4/6] fix: MR feedback. Remove public beta & other fixes.

---
 .gitlab-ci.yml | 102 ++++++++++++++++++++++++++-----------------------
 README.md      |  16 ++------
 2 files changed, 57 insertions(+), 61 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fc8593b4..b0a69779 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,8 +5,10 @@ stages:
   - staging
   - production
 
+
 image: urgi/docker-browsers:latest
 
+
 # Disable the Gradle daemon for Continuous Integration servers as correctness
 # is usually a priority over speed in CI environments. Using a fresh
 # runtime for each build is more reliable since the runtime is completely
@@ -17,6 +19,7 @@ variables:
   APP_NAME: gpds
   JAR_PATH: "backend/build/libs/${APP_NAME}.jar"
 
+
 # Gradle cache for all jobs
 cache:
   key: "$CI_COMMIT_REF_NAME"
@@ -24,14 +27,16 @@ cache:
     - ".gradle"
     - "frontend/.gradle/"
     - "frontend/node_modules/"
-    - "$JAR_PATH"
+
 
 # TESTS
 
+
 lint:
   stage: test
   script: "./gradlew lint"
 
+
 test-frontend:
   stage: test
   script: "./gradlew :frontend:test --parallel"
@@ -40,6 +45,7 @@ test-frontend:
       junit:
         - "./frontend/karma-junit-tests-report/TEST*.xml"
 
+
 test-backend:
   stage: test
   script: "./gradlew :backend:test --parallel"
@@ -62,8 +68,10 @@ test-backend:
       junit:
         - "./backend/build/test-results/test/TEST-*.xml"
 
+
 # BUILD
 
+
 build:
   stage: build
   script:
@@ -78,8 +86,10 @@ build:
       - backend/src/**/*
       - frontend/**/*
 
+
 # DEPLOY
 
+
 .deploy-to-vm: &deploy_to_vm
   # Hidden job which serves as template for executed jobs below.
   # See https://docs.gitlab.com/ee/ci/yaml/#anchors
@@ -90,31 +100,28 @@ build:
     - ssh-add <(echo "${SSH_PRIVATE_KEY}")
     - ssh -o StrictHostKeyChecking=no root@${SERVER_IP} 'echo "Successfully connected on $(hostname)"'
   script:
-    # Copy jar and data (cleaning them before) to the server
-    - "scp ./$JAR_PATH root@${SERVER_IP}:/opt/${APP_NAME}/${APP_NAME}-${ENV}.jar"
+    # Copy jar
+    - scp ./$JAR_PATH root@${SERVER_IP}:/opt/${APP_NAME}/${APP_NAME}-${ENV}.jar
     # Restarting service with the updated jar and the according Spring profiles enabled
-    - "ssh root@${SERVER_IP} \"systemctl restart ${APP_NAME}-${ENV}\""
+    - ssh root@${SERVER_IP} "systemctl restart ${APP_NAME}@${ENV}"
     - eval $(ssh-agent -k)
-    - "echo \"Deploy done. Application should be available at: http://${SERVER_IP}:${APP_PORT}/${CONTEXT_PATH} \""
-  only:
-    changes:
-      - .gitlab-ci.yml
-      - backend/src/**/*
-      - frontend/**/*
+    - echo "Deploy done. Application should be available at http://${SERVER_IP}:${APP_PORT}/${CONTEXT_PATH}"
 
 
-deploy-to-prod:
-  stage: production
+deploy-to-staging:
+  stage: staging
   variables:
-    ENV: prod
-    APP_PORT: ${PROD_GPDS_PORT}
-    CONTEXT_PATH: ${PROD_GPDS_CONTEXT_PATH}
+    ENV: staging
+    APP_PORT: ${STAGING_GPDS_PORT}
+    CONTEXT_PATH: ${STAGING_GPDS_CONTEXT_PATH}
   <<: *deploy_to_vm
   only:
     refs:
       - master
-  when: manual
-  allow_failure: false
+    changes:
+      - .gitlab-ci.yml
+      - backend/src/**/*
+      - frontend/**/*
 
 
 deploy-to-beta:
@@ -127,64 +134,63 @@ deploy-to-beta:
   only:
     refs:
       - branches
-  except:
-    refs:
-      - master
+    changes:
+      - .gitlab-ci.yml
+      - backend/src/**/*
+      - frontend/**/*
+  when: manual
   allow_failure: false
 
 
-deploy-to-staging:
-  stage: staging
-  variables:
-    ENV: staging
-    APP_PORT: ${STAGING_GPDS_PORT}
-    CONTEXT_PATH: ${STAGING_GPDS_CONTEXT_PATH}
-  <<: *deploy_to_vm
-  only:
-    refs:
-      - master
-
-
-deploy-to-private-prod:
+deploy-to-int:
   stage: production
   variables:
-    ENV: private-prod
-    APP_PORT: ${PRIVATE_PROD_GPDS_PORT}
-    CONTEXT_PATH: ${PRIVATE_PROD_GPDS_CONTEXT_PATH}
+    ENV: int
+    APP_PORT: ${INT_GPDS_PORT}
+    CONTEXT_PATH: ${INT_GPDS_CONTEXT_PATH}
   <<: *deploy_to_vm
   only:
     refs:
       - master
+    changes:
+      - .gitlab-ci.yml
+      - backend/src/**/*
+      - frontend/**/*
   when: manual
   allow_failure: false
 
 
-deploy-to-private-int:
+deploy-to-public-prod:
   stage: production
   variables:
-    ENV: private-int
-    APP_PORT: ${PRIVATE_INT_GPDS_PORT}
-    CONTEXT_PATH: ${PRIVATE_INT_GPDS_CONTEXT_PATH}
+    ENV: public-prod
+    APP_PORT: ${PUBLIC_PROD_GPDS_PORT}
+    CONTEXT_PATH: ${PUBLIC_PROD_GPDS_CONTEXT_PATH}
   <<: *deploy_to_vm
   only:
     refs:
       - master
+    changes:
+      - .gitlab-ci.yml
+      - backend/src/**/*
+      - frontend/**/*
   when: manual
   allow_failure: false
 
 
-deploy-to-private-beta:
-  stage: staging
+deploy-to-private-prod:
+  stage: production
   variables:
-    ENV: private-beta
-    APP_PORT: ${PRIVATE_BETA_GPDS_PORT}
-    CONTEXT_PATH: ${PRIVATE_BETA_GPDS_CONTEXT_PATH}
+    ENV: private-prod
+    APP_PORT: ${PRIVATE_PROD_GPDS_PORT}
+    CONTEXT_PATH: ${PRIVATE_PROD_GPDS_CONTEXT_PATH}
   <<: *deploy_to_vm
   only:
-    refs:
-      - branches
-  except:
     refs:
       - master
+    changes:
+      - .gitlab-ci.yml
+      - backend/src/**/*
+      - frontend/**/*
   when: manual
   allow_failure: false
diff --git a/README.md b/README.md
index 246c9c0b..24bc7165 100644
--- a/README.md
+++ b/README.md
@@ -105,7 +105,7 @@ The details of this remote server are filled in the `bootstrap.yml` file.
 By default, it tries to connect to the remote server on http://localhost:8888
 but it can of course be changed, or even configured via the `SPRING_CONFIG_URI` environment variable.
 
-It will try to fetch the configuration for the application name `rare`, and the default profile.
+It will try to fetch the configuration for the application name `gpds`, and the default profile.
 If such a configuration is not found, it will then fallback to the local `application.yml` properties.
 To avoid running the Spring Cloud config server every time when developing the application,
 all the properties are still available in `application.yml` even if they are configured on the remote Spring Cloud server as well.
@@ -114,7 +114,7 @@ If you want to use the Spring Cloud config app locally,
 see https://forgemia.inra.fr/urgi-is/data-discovery-config
 
 The configuration is currently only read on startup,
-meaning the application has to be reboot if the configuration is changed on the Spring Cloud server.
+meaning the application has to be rebooted if the configuration is changed on the Spring Cloud server.
 For a dynamic reload without restarting the application,
 see http://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#refresh-scope
 to check what has to be changed.
@@ -124,14 +124,4 @@ and append the `--spring.cloud.config.label=<branch name to test>` parameter whe
 More info on how pass a parameter to a Spring Boot app:
 https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config
 
-### Application port & context path
-
-|                | GPDS GnpIS               |
-| :------------: | :----------------------  |
-| dev            | :8060/gnpis/gpds         |
-| beta           | :8061/gnpis/gpds-beta    |
-| staging        | :8062/staging/gnpis/gpds |
-| prod           | :8063/gnpis/gpds         |
-| private prod   | :8064/private/gnpis/gpds |
-| private beta   | :8065/beta/gnpis/gpds    |
-| private int    | :8066/int/gnpis/gpds     |
+See the [`data-discovery-config` README.md file](https://forgemia.inra.fr/urgi-is/data-discovery-config/blob/master/README.md) for the list of application environments (server ports and context paths).
-- 
GitLab


From f54eeba94af7e6749c9bc9a9913bb4f7cfa9d3e5 Mon Sep 17 00:00:00 2001
From: gcornut <guillaume.cornut@inra.fr>
Date: Mon, 18 Feb 2019 16:14:45 +0100
Subject: [PATCH 5/6] fix: Remove unnecessary spring profile. Fix angular base
 href.

---
 README.md                                     |  12 +-
 .../urgi/gpds/filter/AngularRouteFilter.java  | 115 ++++++++++++++++++
 .../gpds/filter/AuthenticationFilter.java     |  11 +-
 .../fr/inra/urgi/gpds/filter/IndexFilter.java |  81 ------------
 backend/src/main/resources/application.yml    |  25 ++--
 backend/src/main/resources/bootstrap.yml      |   5 +-
 .../gpds/filter/AngularRouteFilterTest.java   | 105 ++++++++++++++++
 .../urgi/gpds/filter/IndexFilterTest.java     |  70 -----------
 frontend/angular.json                         |   2 +-
 frontend/proxy.conf.js                        |   2 +-
 10 files changed, 237 insertions(+), 191 deletions(-)
 create mode 100644 backend/src/main/java/fr/inra/urgi/gpds/filter/AngularRouteFilter.java
 delete mode 100644 backend/src/main/java/fr/inra/urgi/gpds/filter/IndexFilter.java
 create mode 100644 backend/src/test/java/fr/inra/urgi/gpds/filter/AngularRouteFilterTest.java
 delete mode 100644 backend/src/test/java/fr/inra/urgi/gpds/filter/IndexFilterTest.java

diff --git a/README.md b/README.md
index 24bc7165..7cec202e 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ Otherwise, for the complete server (backend APIs + frontend interface), you can
 ./gradlew assemble && java -jar backend/build/libs/gpds.jar
 ```
 
-The server should then be accessible at http://localhost:8060/gnpis/gpds
+The server should then be accessible at http://localhost:8380/gnpis/gpds
 
 ## Run frontend development server
 
@@ -110,18 +110,12 @@ If such a configuration is not found, it will then fallback to the local `applic
 To avoid running the Spring Cloud config server every time when developing the application,
 all the properties are still available in `application.yml` even if they are configured on the remote Spring Cloud server as well.
 
-If you want to use the Spring Cloud config app locally,
-see https://forgemia.inra.fr/urgi-is/data-discovery-config
-
 The configuration is currently only read on startup,
 meaning the application has to be rebooted if the configuration is changed on the Spring Cloud server.
 For a dynamic reload without restarting the application,
 see http://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#refresh-scope
 to check what has to be changed.
 
-In case of testing configuration from the config server, one may use a dedicated branch on `data-discovery-config` project
-and append the `--spring.cloud.config.label=<branch name to test>` parameter when starting the application's executable jar.
-More info on how pass a parameter to a Spring Boot app:
-https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config
+If you want to use a Spring Cloud configuration server, please refer to
+https://spring.io/guides/gs/centralized-configuration/
 
-See the [`data-discovery-config` README.md file](https://forgemia.inra.fr/urgi-is/data-discovery-config/blob/master/README.md) for the list of application environments (server ports and context paths).
diff --git a/backend/src/main/java/fr/inra/urgi/gpds/filter/AngularRouteFilter.java b/backend/src/main/java/fr/inra/urgi/gpds/filter/AngularRouteFilter.java
new file mode 100644
index 00000000..d307c542
--- /dev/null
+++ b/backend/src/main/java/fr/inra/urgi/gpds/filter/AngularRouteFilter.java
@@ -0,0 +1,115 @@
+package fr.inra.urgi.gpds.filter;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Filter that intercepts all request to potential Angular routes
+ * (ex: /studies/ID) to send back the Angular `index.html` file with a correct
+ * base href set to the spring server context path.
+ *
+ * Potential angular routes are devised by process of elimination:
+ * - They should be GET requests
+ * - They should not end with common static file suffixes {@link AngularRouteFilter#STATIC_SUFFIXES}
+ * - They should not start with API prefixes {@link AngularRouteFilter#API_PREFIXES}
+ *
+ * <p>
+ * Adapted from data-discovery
+ *
+ * @author gcornut
+ */
+@Component
+@WebFilter("/*")
+public class AngularRouteFilter implements Filter {
+
+    private static final String[] API_PREFIXES = {
+        "/brapi/v1", "/gnpis/v1", "/actuator", "/v2/api-docs", "/swagger-resources"
+    };
+
+    private static final String[] STATIC_SUFFIXES = {
+        ".html", ".js", ".css", ".ico", ".png", ".jpg", ".gif", ".eot", ".svg",
+        ".woff2", ".ttf", ".woff"
+    };
+
+    @Value("${server.servlet.context-path}")
+    private String serverContextPath;
+
+    private final ResourceLoader resourceLoader;
+
+    @Autowired
+    public AngularRouteFilter(ResourceLoader resourceLoader) {
+        this.resourceLoader = resourceLoader;
+    }
+
+    @Override
+    public void doFilter(
+        ServletRequest req,
+        ServletResponse response,
+        FilterChain chain
+    ) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) req;
+
+        if (isAngularRoute(request)) {
+            // Angular route
+            InputStream inputStream = resourceLoader.getResource("classpath:static/index.html").getInputStream();
+
+            ByteSource byteSource = new ByteSource() {
+                @Override
+                public InputStream openStream() {
+                    return inputStream;
+                }
+            };
+
+            String content = byteSource.asCharSource(Charsets.UTF_8).read();
+            String replacedContent = content.replace(
+                "<base href=\"./\">",
+                "<base href=\"" + serverContextPath + "/\">"
+            );
+            response.getWriter().write(replacedContent);
+            return;
+        }
+
+        // Otherwise nothing to do
+        chain.doFilter(request, response);
+    }
+
+    private boolean isAngularRoute(HttpServletRequest request) {
+        if (!request.getMethod().equals("GET")) {
+            return false;
+        }
+
+        String fullUri = request.getRequestURI();
+        String contextPath = request.getContextPath();
+        String uri = fullUri.substring(contextPath.length());
+
+        return !isApiOrStaticResource(uri);
+    }
+
+    private boolean isApiOrStaticResource(String relativePath) {
+        // Starts with API prefix
+        return Arrays.stream(API_PREFIXES).anyMatch(relativePath::startsWith)
+            // or has static file suffix
+            || Arrays.stream(STATIC_SUFFIXES).anyMatch(relativePath::endsWith);
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) {
+        // nothing to do
+    }
+
+    @Override
+    public void destroy() {
+        // nothing to do
+    }
+}
diff --git a/backend/src/main/java/fr/inra/urgi/gpds/filter/AuthenticationFilter.java b/backend/src/main/java/fr/inra/urgi/gpds/filter/AuthenticationFilter.java
index f6f2a6cd..9a6c62cb 100644
--- a/backend/src/main/java/fr/inra/urgi/gpds/filter/AuthenticationFilter.java
+++ b/backend/src/main/java/fr/inra/urgi/gpds/filter/AuthenticationFilter.java
@@ -40,15 +40,6 @@ public class AuthenticationFilter implements Filter {
     public void doFilter(
         ServletRequest req, ServletResponse resp, FilterChain chain
     ) throws IOException, ServletException {
-        // get web login
-        String webUserLogin = ((HttpServletRequest) req).getRemoteUser();
-        logger.debug(
-            "\n" +
-                "*********************************************\n" +
-                " Applying user credentials for " + webUserLogin + "\n" +
-                "*********************************************"
-        );
-
         final String authorization = ((HttpServletRequest) req).getHeader("Authorization");
 
         if (authorization != null && authorization.startsWith("Basic")) {
@@ -57,6 +48,8 @@ public class AuthenticationFilter implements Filter {
             String authCode = new String(BaseEncoding.base64().decode(base64Credentials), Charsets.UTF_8);
             final String userName = authCode.split(":", 2)[0];
 
+            logger.debug("Intercepting HTTP Authorization with user: " + userName);
+
             AuthenticationStore.set(userName);
         }
 
diff --git a/backend/src/main/java/fr/inra/urgi/gpds/filter/IndexFilter.java b/backend/src/main/java/fr/inra/urgi/gpds/filter/IndexFilter.java
deleted file mode 100644
index 0cd802ec..00000000
--- a/backend/src/main/java/fr/inra/urgi/gpds/filter/IndexFilter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package fr.inra.urgi.gpds.filter;
-
-import org.springframework.stereotype.Component;
-
-import javax.servlet.*;
-import javax.servlet.annotation.WebFilter;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.util.Arrays;
-
-/**
- * Filter that forwards all GET requests to non-static and non-api resources to index.html. This filter is necessary
- * to support deep-linking for URLs generated by the Angular router.
- * <p>
- * Adapted from data-discovery
- *
- * @author gcornut
- */
-@Component
-@WebFilter("/*")
-public class IndexFilter implements Filter {
-
-    private static final String[] API_PREFIXES = {
-        "/brapi/v1", "/gnpis/v1", "/actuator", "/api-docs", "/v2/api-docs", "/swagger-resources"
-    };
-
-    private static final String[] STATIC_FILES = {
-        "/index.html", "/swagger-ui.html"
-    };
-
-    private static final String[] STATIC_SUFFIXES = {
-        ".js", ".css", ".ico", ".png", ".jpg", ".gif", ".eot", ".svg",
-        ".woff2", ".ttf", ".woff"
-    };
-
-    @Override
-    public void doFilter(
-        ServletRequest req,
-        ServletResponse response,
-        FilterChain chain
-    ) throws IOException, ServletException {
-        HttpServletRequest request = (HttpServletRequest) req;
-        if (mustForward(request)) {
-            request.getRequestDispatcher("/index.html").forward(request, response);
-            return;
-        }
-
-        chain.doFilter(request, response);
-    }
-
-    private boolean mustForward(HttpServletRequest request) {
-        if (!request.getMethod().equals("GET")) {
-            return false;
-        }
-
-        String fullUri = request.getRequestURI();
-        String contextPath = request.getContextPath();
-        String uri = fullUri.substring(contextPath.length());
-
-        return !isApiOrStaticResource(uri);
-    }
-
-    private boolean isApiOrStaticResource(String uri) {
-        // Starts with API prefix
-        return Arrays.stream(API_PREFIXES).anyMatch(uri::startsWith)
-            // or is static file
-            || Arrays.asList(STATIC_FILES).contains(uri)
-            // or has static file suffix
-            || Arrays.stream(STATIC_SUFFIXES).anyMatch(uri::endsWith);
-    }
-
-    @Override
-    public void init(FilterConfig filterConfig) {
-        // nothing to do
-    }
-
-    @Override
-    public void destroy() {
-        // nothing to do
-    }
-}
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index 02cfa4f3..ebe053c7 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -12,15 +12,6 @@ management:
       exposure:
         include: '*'
 
-server:
-  compression:
-    enabled: true
-    mime-types:
-      - application/json
-      - application/javascript
-      - text/html
-      - text/css
-
 logging.level:
   root: ERROR
   org.springframework:
@@ -28,11 +19,6 @@ logging.level:
     web.client.RestTemplate: DEBUG
   fr.inra: DEBUG
 
----
-spring:
-  profiles: gnpis
-  cloud.config.name: gpds
-
 gpds:
   elasticsearch-alias-template:
     gnpis_{source}_{documentType}_5432_scratchy-group{groupId}
@@ -52,7 +38,14 @@ gpds:
   security-user-group-ws-token:
 
 server:
-  port: 8060
+  compression:
+    enabled: true
+    mime-types:
+      - application/json
+      - application/javascript
+      - text/html
+      - text/css
+  port: 8380
   servlet:
-    context-path: /gnpis/gpds
+    context-path: /gnpis-dev/gpds
 
diff --git a/backend/src/main/resources/bootstrap.yml b/backend/src/main/resources/bootstrap.yml
index 71788dfe..9e6ab0a5 100644
--- a/backend/src/main/resources/bootstrap.yml
+++ b/backend/src/main/resources/bootstrap.yml
@@ -1,8 +1,5 @@
 spring:
-  application:
-    name: gpds
+  application.name: gpds
   cloud:
     config:
       uri: ${SPRING_CONFIG_URI:http://localhost:8888}
-  profiles:
-    active: gnpis
diff --git a/backend/src/test/java/fr/inra/urgi/gpds/filter/AngularRouteFilterTest.java b/backend/src/test/java/fr/inra/urgi/gpds/filter/AngularRouteFilterTest.java
new file mode 100644
index 00000000..12043dc7
--- /dev/null
+++ b/backend/src/test/java/fr/inra/urgi/gpds/filter/AngularRouteFilterTest.java
@@ -0,0 +1,105 @@
+package fr.inra.urgi.gpds.filter;
+
+import fr.inra.urgi.gpds.Application;
+import fr.inra.urgi.gpds.config.SecurityConfig;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.io.ByteArrayInputStream;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
+
+/**
+ * Unit tests for {@link AngularRouteFilter}
+ *
+ * @author gcornut
+ */
+@ExtendWith(SpringExtension.class)
+@Import(SecurityConfig.class)
+@SpringBootTest(classes = Application.class)
+class AngularRouteFilterTest {
+
+    @Autowired
+    private WebApplicationContext context;
+
+    @MockBean
+    private ResourceLoader resourceLoader;
+
+    private MockMvc mockMvc;
+
+    private AngularRouteFilter filter;
+
+    @BeforeEach
+    void setUp() {
+        filter = new AngularRouteFilter(resourceLoader);
+        mockMvc = MockMvcBuilders.webAppContextSetup(context)
+            .addFilter(filter, "/*")
+            .build();
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+        // Static files
+        "/index.html",
+        "/script.js",
+        "/style.css",
+        "/image.gif",
+        "/icon.ico",
+        "/image.png",
+        "/image.jpg",
+        "/font.woff",
+        "/font.ttf",
+        // APIs
+        "/brapi/v1/studies",
+        "/gnpis/v1/datadiscovery/suggest",
+        "/actuator/info",
+    })
+    void shouldNotForward(String url) throws Exception {
+        mockMvc.perform(get(url)).andExpect(forwardedUrl(null));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+        "/home",
+        "/studies/foo",
+        "/germplasm/bar",
+    })
+    void shouldForward(String url) throws Exception {
+        String indexBefore = "<html>\n" +
+            "  <base href=\"./\">\n" +
+            "</html>";
+        String indexAfter = "<html>\n" +
+            "  <base href=\"/gnpis-test/gpds/\">\n" +
+            "</html>";
+
+        ReflectionTestUtils.setField(filter, "serverContextPath", "/gnpis-test/gpds");
+
+        Resource mockResource = mock(Resource.class);
+        when(mockResource.getInputStream())
+            .thenReturn(new ByteArrayInputStream(indexBefore.getBytes()));
+        when(resourceLoader.getResource(anyString()))
+            .thenReturn(mockResource);
+
+        mockMvc.perform(get(url))
+            .andExpect(content().string(indexAfter));
+    }
+
+}
diff --git a/backend/src/test/java/fr/inra/urgi/gpds/filter/IndexFilterTest.java b/backend/src/test/java/fr/inra/urgi/gpds/filter/IndexFilterTest.java
deleted file mode 100644
index 9649287f..00000000
--- a/backend/src/test/java/fr/inra/urgi/gpds/filter/IndexFilterTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package fr.inra.urgi.gpds.filter;
-
-import fr.inra.urgi.gpds.api.gnpis.v1.DataDiscoveryController;
-import fr.inra.urgi.gpds.config.SecurityConfig;
-import fr.inra.urgi.gpds.repository.es.DataDiscoveryRepository;
-import fr.inra.urgi.gpds.repository.file.DataSourceRepository;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Import;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-import org.springframework.test.web.servlet.MockMvc;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
-
-/**
- * Unit tests for {@link IndexFilter}
- *
- * @author gcornut
- */
-@ExtendWith(SpringExtension.class)
-@WebMvcTest(controllers = DataDiscoveryController.class)
-@Import(SecurityConfig.class)
-class IndexFilterTest {
-
-    @MockBean
-    private DataDiscoveryRepository repository;
-
-    @MockBean
-    private DataSourceRepository dataSourceRepository;
-
-    @Autowired
-    private MockMvc mockMvc;
-
-    @ParameterizedTest
-    @ValueSource(strings = {
-        // Static files
-        "/index.html",
-        "/script.js",
-        "/style.css",
-        "/image.gif",
-        "/icon.ico",
-        "/image.png",
-        "/image.jpg",
-        "/font.woff",
-        "/font.ttf",
-        // APIs
-        "/brapi/v1/studies",
-        "/gnpis/v1/datadiscovery/suggest",
-        "/actuator/info",
-    })
-    void shouldNotForward(String url) throws Exception {
-        mockMvc.perform(get(url)).andExpect(forwardedUrl(null));
-    }
-
-    @ParameterizedTest
-    @ValueSource(strings = {
-        "/home",
-        "/studies/foo",
-        "/germplasm/bar",
-    })
-    void shouldForward(String url) throws Exception {
-        mockMvc.perform(get(url)).andExpect(forwardedUrl("/index.html"));
-    }
-
-}
diff --git a/frontend/angular.json b/frontend/angular.json
index bd667ae3..b3634382 100644
--- a/frontend/angular.json
+++ b/frontend/angular.json
@@ -42,7 +42,7 @@
               "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
             ],
             "scripts": [],
-            "baseHref": "/gnpis/gpds/"
+            "baseHref": "./"
           },
           "configurations": {
             "production": {
diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js
index d5aee375..df13c009 100644
--- a/frontend/proxy.conf.js
+++ b/frontend/proxy.conf.js
@@ -4,7 +4,7 @@ const PROXY_CONFIG = [
             "/gnpis/gpds/brapi",
             "/gnpis/gpds/gnpis",
         ],
-        target: "http://localhost:8060",
+        target: "http://localhost:8380",
         secure: false
     }
 ];
-- 
GitLab


From b5dd81ea393a8c58cee332ded4bf5570b19354fa Mon Sep 17 00:00:00 2001
From: gcornut <guillaume.cornut@inra.fr>
Date: Thu, 21 Feb 2019 19:24:30 +0100
Subject: [PATCH 6/6] fix: Fix Angular PROD and DEV context path switching.

---
 frontend/angular.json      |  9 +++++----
 frontend/package-lock.json | 13 +++++++++++++
 frontend/proxy.conf.js     |  4 ++--
 3 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/frontend/angular.json b/frontend/angular.json
index b3634382..6a6ee052 100644
--- a/frontend/angular.json
+++ b/frontend/angular.json
@@ -42,7 +42,7 @@
               "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
             ],
             "scripts": [],
-            "baseHref": "./"
+            "baseHref": "/gnpis-dev/gpds/"
           },
           "configurations": {
             "production": {
@@ -64,15 +64,16 @@
               "budgets": [
                 {
                   "type": "initial",
-                  "maximumWarning": "1500kb",
-                  "maximumError": "2000kb"
+                  "maximumWarning": "2000kb",
+                  "maximumError": "3000kb"
                 }
               ],
               "stylePreprocessorOptions": {
                 "includePaths": [
                   "src/assets/gpds/"
                 ]
-              }
+              },
+            "baseHref": "./"
             }
           }
         },
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 14a78b22..0a1e81ce 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -6757,6 +6757,14 @@
       "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
       "dev": true
     },
+    "ngx-moment": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/ngx-moment/-/ngx-moment-3.3.0.tgz",
+      "integrity": "sha512-6fpllpJqLfjRWboOhphgeEYt+rzIA9O29rG5QWCebRt2X0uNk4P93sLEb0S8lbDF0dEp2NOC3UOD+xoCVlJQhA==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "ngx-speculoos": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/ngx-speculoos/-/ngx-speculoos-1.1.0.tgz",
@@ -7495,6 +7503,11 @@
         "find-up": "^2.1.0"
       }
     },
+    "popper.js": {
+      "version": "1.14.6",
+      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz",
+      "integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA=="
+    },
     "portfinder": {
       "version": "1.0.17",
       "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz",
diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js
index df13c009..f271334b 100644
--- a/frontend/proxy.conf.js
+++ b/frontend/proxy.conf.js
@@ -1,8 +1,8 @@
 const PROXY_CONFIG = [
     {
         context: [
-            "/gnpis/gpds/brapi",
-            "/gnpis/gpds/gnpis",
+            "/gnpis-dev/gpds/brapi",
+            "/gnpis-dev/gpds/gnpis",
         ],
         target: "http://localhost:8380",
         secure: false
-- 
GitLab