diff --git a/client/src/app/admin/admin-auth.guard.ts b/client/src/app/admin/admin-auth.guard.ts
index fb9fab7fcd5c49127b00f022523f59ee4baf891b..02ac7a5e6dea1d5f36f8abb33b1a912d2c75d9e5 100644
--- a/client/src/app/admin/admin-auth.guard.ts
+++ b/client/src/app/admin/admin-auth.guard.ts
@@ -8,47 +8,52 @@
  */
 
 import { Injectable } from '@angular/core';
-import {
-    ActivatedRouteSnapshot,
-    Router,
-    RouterStateSnapshot,
-} from '@angular/router';
-import { Store } from '@ngrx/store';
+import { CanActivate, Router } from '@angular/router';
+import { Store, select } from '@ngrx/store';
 
-import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
+import { combineLatest, Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
 
 import * as authActions from 'src/app/auth/auth.actions';
-import { environment } from 'src/environments/environment';
+import * as authSelector from 'src/app/auth/auth.selector';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable({
     providedIn: 'root',
 })
-export class AdminAuthGuard extends KeycloakAuthGuard {
+export class AdminAuthGuard implements CanActivate {
     constructor(
         protected readonly router: Router,
-        protected readonly keycloak: KeycloakService,
-        private store: Store<{ }>
-    ) {
-        super(router, keycloak);
-    }
+        private store: Store<{ }>,
+        private config: AppConfigService
+    ) { }
+
+    canActivate(): Observable<boolean> {
+        return combineLatest([
+            this.store.pipe(select(authSelector.selectUserRoles)),
+            this.store.pipe(select(authSelector.selectIsAuthenticated))
+        ]).pipe(
+            map(([userRoles, isAuthenticated]) => {
+                // Auth disabled
+                if (!this.config.authenticationEnabled) {
+                    return true;
+                }
+
+                // Force the user to log in if currently unauthenticated.
+                if (!isAuthenticated) {
+                    this.store.dispatch(authActions.login());
+                    return false;
+                }
+
+                // If authenticated but not admin go to unauthorized page.
+                if (!userRoles.includes(this.config.adminRole)) {
+                    this.router.navigateByUrl('/unauthorized');
+                    return false;
+                }
 
-    public async isAccessAllowed(
-        route: ActivatedRouteSnapshot,
-        state: RouterStateSnapshot
-    ) {
-        // Force the user to log in if currently unauthenticated.
-        if (!this.authenticated) {
-            this.store.dispatch(authActions.login());
-            return false;
-        }
-
-        // If authenticated but not admin go to unauthorized page.
-        if (!this.roles.includes(environment.adminRole)) {
-            this.router.navigateByUrl('/unauthorized');
-            return false;
-        }
-
-        // Else return true;
-        return true;
+                // Let "Router" allow user entering the page
+                return true;
+            })
+        );
     }
 }
diff --git a/client/src/app/admin/admin-routing.module.ts b/client/src/app/admin/admin-routing.module.ts
index 7fc65a514fb2586831625291e2eefc1804921970..d3aef6a6b6acd59c9bd6da9d15e7d601e3b34eeb 100644
--- a/client/src/app/admin/admin-routing.module.ts
+++ b/client/src/app/admin/admin-routing.module.ts
@@ -29,11 +29,10 @@ import { DatabaseListComponent } from './containers/database/database-list.compo
 import { NewDatabaseComponent } from './containers/database/new-database.component';
 import { EditDatabaseComponent } from './containers/database/edit-database.component';
 import { SettingsComponent } from './containers/settings/settings.component';
-import { environment } from 'src/environments/environment';
 
 const routes: Routes = [
     { 
-        path: '', component: AdminComponent, children: [
+        path: '', component: AdminComponent, canActivate: [AdminAuthGuard], children: [
             { path: '', redirectTo: 'instance-list', pathMatch: 'full' },
             { path: 'instance-list', component: InstanceListComponent },
             { path: 'new-instance', component: NewInstanceComponent },
@@ -57,11 +56,6 @@ const routes: Routes = [
     }
 ];
 
-// Add auth guard if authentication is activated
-if (environment.authenticationEnabled) {
-    routes[0].canActivate = [ AdminAuthGuard ];
-}
-
 @NgModule({
     imports: [RouterModule.forChild(routes)],
     exports: [RouterModule]
diff --git a/client/src/app/admin/admin.component.html b/client/src/app/admin/admin.component.html
index 979df50e47db62138793be21dff9adf7df5e4cf1..61101934f1e33d3d27823eb8998cedcf423d891b 100644
--- a/client/src/app/admin/admin.component.html
+++ b/client/src/app/admin/admin.component.html
@@ -3,6 +3,8 @@
         [links]="links"
         [isAuthenticated]="isAuthenticated | async"
         [userProfile]="userProfile | async"
+        [baseHref]="getBaseHref()"
+        [authenticationEnabled]="getAuthenticationEnabled()"
         (login)="login()"
         (logout)="logout()"
         (openEditProfile)="openEditProfile()">
diff --git a/client/src/app/admin/admin.component.ts b/client/src/app/admin/admin.component.ts
index 18364e145b246373d137a3a0d84bcfa84ec667d7..93e97de10f1e36607d482147e840a4567f1e5e4c 100644
--- a/client/src/app/admin/admin.component.ts
+++ b/client/src/app/admin/admin.component.ts
@@ -14,6 +14,7 @@ import { Store } from '@ngrx/store';
 import { UserProfile } from 'src/app/auth/user-profile.model';
 import * as authActions from 'src/app/auth/auth.actions';
 import * as authSelector from 'src/app/auth/auth.selector';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-admin',
@@ -37,12 +38,20 @@ export class AdminComponent {
     public userProfile: Observable<UserProfile>;
     public userRoles: Observable<string[]>;
 
-    constructor(private store: Store<{ }>) {
+    constructor(private store: Store<{ }>, private config: AppConfigService) {
         this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
         this.userProfile = store.select(authSelector.selectUserProfile);
         this.userRoles = store.select(authSelector.selectUserRoles);
     }
 
+    getBaseHref() {
+        return this.config.baseHref;
+    }
+
+    getAuthenticationEnabled() {
+        return this.config.authenticationEnabled;
+    }
+
     login(): void {
         this.store.dispatch(authActions.login());
     }
diff --git a/client/src/app/admin/components/attribute/add-attribute.component.ts b/client/src/app/admin/components/attribute/add-attribute.component.ts
index f7934d61f05212f998fcdcf66c10556de52e0046..fc050468a347ea1eb1ded1d298b01fdcbb1fd24b 100644
--- a/client/src/app/admin/components/attribute/add-attribute.component.ts
+++ b/client/src/app/admin/components/attribute/add-attribute.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, TemplateRef } from '@angular/core';
 
 import { BsModalService } from 'ngx-bootstrap/modal';
diff --git a/client/src/app/admin/components/attribute/criteria/generate-option-list.component.ts b/client/src/app/admin/components/attribute/criteria/generate-option-list.component.ts
index 1b6905a2bbcd1f55707332bf561aa085ea78005d..cc0899ce9fea358fc0b06e4a8fc576a06518e951 100644
--- a/client/src/app/admin/components/attribute/criteria/generate-option-list.component.ts
+++ b/client/src/app/admin/components/attribute/criteria/generate-option-list.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, TemplateRef } from '@angular/core';
 
 import { BsModalService } from 'ngx-bootstrap/modal';
diff --git a/client/src/app/admin/components/attribute/criteria/option-form.component.ts b/client/src/app/admin/components/attribute/criteria/option-form.component.ts
index 5e2a620d0a6985db4690cf84108b4e59b85d2ee2..cff2bdcdf35a3401fad6adf163e80240c75cdd84 100644
--- a/client/src/app/admin/components/attribute/criteria/option-form.component.ts
+++ b/client/src/app/admin/components/attribute/criteria/option-form.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 
diff --git a/client/src/app/admin/components/attribute/criteria/option-list.component.ts b/client/src/app/admin/components/attribute/criteria/option-list.component.ts
index 492bac069a3bfccdd9e8a9f3846a168651c1281b..c3e643a5a7c5cf9b145970aeb5853227a86f1139 100644
--- a/client/src/app/admin/components/attribute/criteria/option-list.component.ts
+++ b/client/src/app/admin/components/attribute/criteria/option-list.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
 import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
 
diff --git a/client/src/app/admin/components/attribute/criteria/table-criteria.component.ts b/client/src/app/admin/components/attribute/criteria/table-criteria.component.ts
index fe5c7db6f47a4db658929ac124354620ee5286f4..9cf5e6dea58fc2c086408e5bbba425aea966a31b 100644
--- a/client/src/app/admin/components/attribute/criteria/table-criteria.component.ts
+++ b/client/src/app/admin/components/attribute/criteria/table-criteria.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component } from '@angular/core';
 
 @Component({
diff --git a/client/src/app/admin/components/attribute/criteria/tr-criteria.component.ts b/client/src/app/admin/components/attribute/criteria/tr-criteria.component.ts
index 87109e23f306817016211fcf8c23f33ee25ba656..9c1f559f4c038fd74a28174071260afdd97c489d 100644
--- a/client/src/app/admin/components/attribute/criteria/tr-criteria.component.ts
+++ b/client/src/app/admin/components/attribute/criteria/tr-criteria.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormArray, FormControl, FormGroup } from '@angular/forms';
 
@@ -6,7 +15,7 @@ import { Attribute, Option, CriteriaFamily, SelectOption } from 'src/app/metamod
 @Component({
     selector: '[criteria]',
     templateUrl: 'tr-criteria.component.html',
-    styleUrls: [ '../tr.component.css' ],
+    styleUrls: [ '../tr.component.scss' ],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class TrCriteriaComponent implements OnInit {
diff --git a/client/src/app/admin/components/attribute/design/index.ts b/client/src/app/admin/components/attribute/design/index.ts
index 97b51ae6380e1ab54108323c607878012b8500c0..e2a18cde45ee5821f4cbbdff63ac2ebf00d12480 100644
--- a/client/src/app/admin/components/attribute/design/index.ts
+++ b/client/src/app/admin/components/attribute/design/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { TableDesignComponent } from './table-design.component';
 import { TrDesignComponent } from './tr-design.component';
 
diff --git a/client/src/app/admin/components/attribute/design/table-design.component.ts b/client/src/app/admin/components/attribute/design/table-design.component.ts
index ece9403ec5149fcac9cddc7211434f8cbbb49ee1..4ca4666de803c2c57e84ff169e20140aad1b5861 100644
--- a/client/src/app/admin/components/attribute/design/table-design.component.ts
+++ b/client/src/app/admin/components/attribute/design/table-design.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component } from '@angular/core';
 
 @Component({
diff --git a/client/src/app/admin/components/attribute/design/tr-design.component.ts b/client/src/app/admin/components/attribute/design/tr-design.component.ts
index 72d1dcedc79ed6c4bde11309d5be0cb3db93df8e..f9a42f6fac4e54b93513e28e5a1365d3747cea2c 100644
--- a/client/src/app/admin/components/attribute/design/tr-design.component.ts
+++ b/client/src/app/admin/components/attribute/design/tr-design.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormControl, FormGroup, Validators } from '@angular/forms';
 
@@ -6,7 +15,7 @@ import { Attribute, SelectOption } from 'src/app/metamodel/models';
 @Component({
     selector: '[design]',
     templateUrl: 'tr-design.component.html',
-    styleUrls: [ '../tr.component.css' ],
+    styleUrls: [ '../tr.component.scss' ],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class TrDesignComponent implements OnInit {
diff --git a/client/src/app/admin/components/attribute/detail/index.ts b/client/src/app/admin/components/attribute/detail/index.ts
index 41a27ae41042126954ad127f19255e1f7f4428ea..d1581ea5b138546e26d7eba21a7cd6d624ce1612 100644
--- a/client/src/app/admin/components/attribute/detail/index.ts
+++ b/client/src/app/admin/components/attribute/detail/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { TableDetailComponent } from './table-detail.component';
 import { TrDetailComponent } from './tr-detail.component';
 
diff --git a/client/src/app/admin/components/attribute/detail/table-detail.component.ts b/client/src/app/admin/components/attribute/detail/table-detail.component.ts
index 5ff070ee6bd57d8971192ca8ec16a1ac16f62e7e..f13fc516276e4de6ed864b1473db48c86d11ced8 100644
--- a/client/src/app/admin/components/attribute/detail/table-detail.component.ts
+++ b/client/src/app/admin/components/attribute/detail/table-detail.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component } from '@angular/core';
 
 @Component({
diff --git a/client/src/app/admin/components/attribute/detail/tr-detail.component.ts b/client/src/app/admin/components/attribute/detail/tr-detail.component.ts
index 2f268277c8ef10cca4a9d1e89afc7871c9fe552b..d485de9dbcd7c5aa3a5b15fa4b3a46824ee3a114 100644
--- a/client/src/app/admin/components/attribute/detail/tr-detail.component.ts
+++ b/client/src/app/admin/components/attribute/detail/tr-detail.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormControl, FormGroup } from '@angular/forms';
 
@@ -6,7 +15,7 @@ import { Attribute, SelectOption } from 'src/app/metamodel/models';
 @Component({
     selector: '[detail]',
     templateUrl: 'tr-detail.component.html',
-    styleUrls: [ '../tr.component.css' ],
+    styleUrls: [ '../tr.component.scss' ],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class TrDetailComponent implements OnInit {
diff --git a/client/src/app/admin/components/attribute/index.ts b/client/src/app/admin/components/attribute/index.ts
index 4d35bcb434eb4bca8dce8b0d06c48966cb889285..fc2f19be83072e0b31e0947c6cacdeeca9ceb864 100644
--- a/client/src/app/admin/components/attribute/index.ts
+++ b/client/src/app/admin/components/attribute/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { AddAttributeComponent } from './add-attribute.component';
 import { designComponents } from './design';
 import { criteriaComponents } from './criteria';
diff --git a/client/src/app/admin/components/attribute/output/index.ts b/client/src/app/admin/components/attribute/output/index.ts
index b5f11cda5869b5136f7e7f02e13f851565f60ef0..1c915eff1cc2d578dd535dd7e6ee51451e80c72b 100644
--- a/client/src/app/admin/components/attribute/output/index.ts
+++ b/client/src/app/admin/components/attribute/output/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { TableOutputComponent } from "./table-output.component";
 import { TrOutputComponent } from "./tr-output.component";
 
diff --git a/client/src/app/admin/components/attribute/output/table-output.component.ts b/client/src/app/admin/components/attribute/output/table-output.component.ts
index 30c36b2babb7851963e557fd76ec19233995529b..a2aefb31b4810d8402e43fdbc87c8f3e4ad21037 100644
--- a/client/src/app/admin/components/attribute/output/table-output.component.ts
+++ b/client/src/app/admin/components/attribute/output/table-output.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component } from '@angular/core';
 
 @Component({
diff --git a/client/src/app/admin/components/attribute/output/tr-output.component.ts b/client/src/app/admin/components/attribute/output/tr-output.component.ts
index a2a7afc994a2711a2acfe186f08b95cb49a85545..f75c845210810abaeaac0395598cb18f9aa2d784 100644
--- a/client/src/app/admin/components/attribute/output/tr-output.component.ts
+++ b/client/src/app/admin/components/attribute/output/tr-output.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormControl, FormGroup } from '@angular/forms';
 
@@ -6,7 +15,7 @@ import { Attribute, OutputCategory } from 'src/app/metamodel/models';
 @Component({
     selector: '[output]',
     templateUrl: 'tr-output.component.html',
-    styleUrls: [ '../tr.component.css' ],
+    styleUrls: [ '../tr.component.scss' ],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class TrOutputComponent implements OnInit {
diff --git a/client/src/app/admin/components/attribute/result/index.ts b/client/src/app/admin/components/attribute/result/index.ts
index 4b25f427fd813bfca6ca81a5bf44c4188f42b4e7..3517973ab6dd103644a98387970d4f37872b6c94 100644
--- a/client/src/app/admin/components/attribute/result/index.ts
+++ b/client/src/app/admin/components/attribute/result/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { TableResultComponent } from './table-result.component';
 import { TrResultComponent } from './tr-result.component';
 import { renderers } from './renderers';
diff --git a/client/src/app/admin/components/attribute/result/renderers/detail-renderer.component.ts b/client/src/app/admin/components/attribute/result/renderers/detail-renderer.component.ts
index 2939aecf082237ddfa821aaa4f28b3b85a09d858..4dfc1917776660fa01cef50275fcd0a25e960abd 100644
--- a/client/src/app/admin/components/attribute/result/renderers/detail-renderer.component.ts
+++ b/client/src/app/admin/components/attribute/result/renderers/detail-renderer.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 
diff --git a/client/src/app/admin/components/attribute/result/renderers/download-renderer.component.ts b/client/src/app/admin/components/attribute/result/renderers/download-renderer.component.ts
index ef1fc0d8dca863f65ac15a0e18d0a87784ea26a6..f3ccfee56acdfe319c922b94a050dc2016f4dbf9 100644
--- a/client/src/app/admin/components/attribute/result/renderers/download-renderer.component.ts
+++ b/client/src/app/admin/components/attribute/result/renderers/download-renderer.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 
diff --git a/client/src/app/admin/components/attribute/result/renderers/image-renderer.component.ts b/client/src/app/admin/components/attribute/result/renderers/image-renderer.component.ts
index 8706711543b54f65eeb80158f5869d8bc2df6fcb..1441a6a8d02d150de426e338c213f575f906b44e 100644
--- a/client/src/app/admin/components/attribute/result/renderers/image-renderer.component.ts
+++ b/client/src/app/admin/components/attribute/result/renderers/image-renderer.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 
diff --git a/client/src/app/admin/components/attribute/result/renderers/index.ts b/client/src/app/admin/components/attribute/result/renderers/index.ts
index bf18afa1c74e0390e22d1d91b826378f7b7fcda2..89c8ed9df366bfeacc2b2d3d5c79a16bb6602c32 100644
--- a/client/src/app/admin/components/attribute/result/renderers/index.ts
+++ b/client/src/app/admin/components/attribute/result/renderers/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { DetailRendererComponent } from './detail-renderer.component';
 import { DownloadRendererComponent } from './download-renderer.component';
 import { ImageRendererComponent } from './image-renderer.component';
diff --git a/client/src/app/admin/components/attribute/result/renderers/link-renderer.component.ts b/client/src/app/admin/components/attribute/result/renderers/link-renderer.component.ts
index a67b880373e3a77de5b3dc03415a095110cd315d..d8d136372b8eaec7e50143324d0f0102ffdeb749 100644
--- a/client/src/app/admin/components/attribute/result/renderers/link-renderer.component.ts
+++ b/client/src/app/admin/components/attribute/result/renderers/link-renderer.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 
diff --git a/client/src/app/admin/components/attribute/result/renderers/renderer-form-factory.ts b/client/src/app/admin/components/attribute/result/renderers/renderer-form-factory.ts
index 8a6e7ef3472d123076d87e9fbe2dde4610e75dd0..22c2f5e30eb422a5fbfc7857615f03988ec254cb 100644
--- a/client/src/app/admin/components/attribute/result/renderers/renderer-form-factory.ts
+++ b/client/src/app/admin/components/attribute/result/renderers/renderer-form-factory.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { FormControl } from "@angular/forms";
 
 export abstract class RendererFormFactory {
diff --git a/client/src/app/admin/components/attribute/result/table-result.component.ts b/client/src/app/admin/components/attribute/result/table-result.component.ts
index d4aa11035eae37eba4bfe62451039fb2175a79b0..86ce85cb110aea655b48802702f6fa3e1b6cff98 100644
--- a/client/src/app/admin/components/attribute/result/table-result.component.ts
+++ b/client/src/app/admin/components/attribute/result/table-result.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component } from '@angular/core';
 
 @Component({
diff --git a/client/src/app/admin/components/attribute/result/tr-result.component.ts b/client/src/app/admin/components/attribute/result/tr-result.component.ts
index 38ae002881cd5b1acc23d3e380b1febeca96df19..fe0aa76ee37d653c1ac16cfb3137b0a3cd59d67b 100644
--- a/client/src/app/admin/components/attribute/result/tr-result.component.ts
+++ b/client/src/app/admin/components/attribute/result/tr-result.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormControl, FormGroup } from '@angular/forms';
 
@@ -7,7 +16,7 @@ import { RendererFormFactory } from './renderers/renderer-form-factory';
 @Component({
     selector: '[result]',
     templateUrl: 'tr-result.component.html',
-    styleUrls: [ '../tr.component.css' ],
+    styleUrls: [ '../tr.component.scss' ],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class TrResultComponent implements OnInit {
diff --git a/client/src/app/admin/components/attribute/tr.component.css b/client/src/app/admin/components/attribute/tr.component.scss
similarity index 100%
rename from client/src/app/admin/components/attribute/tr.component.css
rename to client/src/app/admin/components/attribute/tr.component.scss
diff --git a/client/src/app/admin/components/attribute/vo/index.ts b/client/src/app/admin/components/attribute/vo/index.ts
index 8b6aa3b64ad95dba7b0ad4323b6f7af7583ad06e..80eb8323179ac219829e543e6319e9c06d040240 100644
--- a/client/src/app/admin/components/attribute/vo/index.ts
+++ b/client/src/app/admin/components/attribute/vo/index.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { TableVoComponent } from './table-vo.component';
 import { TrVoComponent } from './tr-vo.component';
 
diff --git a/client/src/app/admin/components/attribute/vo/table-vo.component.ts b/client/src/app/admin/components/attribute/vo/table-vo.component.ts
index ba4352e033e9f7ef300d331b041dd005dafd6b51..57df81c8f7f727ff283bd08e1732d19948e55a4b 100644
--- a/client/src/app/admin/components/attribute/vo/table-vo.component.ts
+++ b/client/src/app/admin/components/attribute/vo/table-vo.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component } from '@angular/core';
 
 @Component({
diff --git a/client/src/app/admin/components/attribute/vo/tr-vo.component.ts b/client/src/app/admin/components/attribute/vo/tr-vo.component.ts
index 521725061e70a4774a6f9658eac64c8787f127f1..e0dd9bb2fa25492df131aed1335612316a594ec8 100644
--- a/client/src/app/admin/components/attribute/vo/tr-vo.component.ts
+++ b/client/src/app/admin/components/attribute/vo/tr-vo.component.ts
@@ -1,3 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormControl, FormGroup } from '@angular/forms';
 
@@ -6,7 +15,7 @@ import { Attribute } from 'src/app/metamodel/models';
 @Component({
     selector: '[vo]',
     templateUrl: 'tr-vo.component.html',
-    styleUrls: [ '../tr.component.css' ],
+    styleUrls: [ '../tr.component.scss' ],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class TrVoComponent implements OnInit {
diff --git a/client/src/app/app-config.service.ts b/client/src/app/app-config.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5944e78a5bc3b8821afd91d1a3b8bc431d643157
--- /dev/null
+++ b/client/src/app/app-config.service.ts
@@ -0,0 +1,13 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class AppConfigService {
+    public apiUrl: string;
+    public servicesUrl: string;
+    public baseHref: string;
+    public authenticationEnabled: boolean;
+    public ssoAuthUrl: string;
+    public ssoRealm: string;
+    public ssoClientId: string;
+    public adminRole: string;
+}
diff --git a/client/src/app/app-init.ts b/client/src/app/app-init.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3dc042681bc40d7d903a4e8585e2349fdff7613
--- /dev/null
+++ b/client/src/app/app-init.ts
@@ -0,0 +1,29 @@
+import { APP_INITIALIZER } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { KeycloakService } from 'keycloak-angular';
+import { Store } from '@ngrx/store';
+
+import { AppConfigService } from './app-config.service';
+import { initializeKeycloak } from 'src/app/auth/init.keycloak';
+
+function appInit(http: HttpClient, appConfigService: AppConfigService, keycloak: KeycloakService, store: Store<{ }>) {
+    return () => {
+        return http.get('/assets/app.config.json')
+            .toPromise()
+            .then(data => {
+                Object.assign(appConfigService, data);
+                return appConfigService;
+            })
+            .then(appConfigService => {
+                return initializeKeycloak(keycloak, store, appConfigService)
+            });
+    }
+}
+
+export const appInitializer = {
+    provide: APP_INITIALIZER,
+    useFactory: appInit,
+    multi: true,
+    deps: [ HttpClient, AppConfigService, KeycloakService, Store ]
+};
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 981c03d269210f348d0572550d13dc939092073a..cca4aece56fc59af597c3db12456c7952748f33d 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -25,6 +25,8 @@ import { CoreModule } from './core/core.module';
 import { AuthModule } from './auth/auth.module';
 import { MetamodelModule } from './metamodel/metamodel.module';
 import { AppComponent } from './core/containers/app.component';
+import { AppConfigService } from './app-config.service';
+import { appInitializer } from './app-init';
 
 @NgModule({
     imports: [
@@ -56,6 +58,10 @@ import { AppComponent } from './core/containers/app.component';
             logOnly: environment.production
         })
     ],
+    providers: [
+        AppConfigService,
+        appInitializer
+    ],
     bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/client/src/app/auth/auth.effects.ts b/client/src/app/auth/auth.effects.ts
index 0f7c6b9cc8c1e02bdc8b7e01c0b4124d900b9e7d..69cefb0e2b4f468564a02cdcbafb631a22113e7f 100644
--- a/client/src/app/auth/auth.effects.ts
+++ b/client/src/app/auth/auth.effects.ts
@@ -15,7 +15,7 @@ import { tap, switchMap } from 'rxjs/operators';
 import { KeycloakService } from 'keycloak-angular';
 
 import * as authActions from './auth.actions';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
  
 @Injectable()
 export class AuthEffects {
@@ -35,8 +35,8 @@ export class AuthEffects {
             ofType(authActions.logout),
             tap(_ => {
                 let redirectUri = window.location.origin;
-                if (environment.baseHref !== '/') {
-                    redirectUri += environment.baseHref;
+                if (this.config.baseHref !== '/') {
+                    redirectUri += this.config.baseHref;
                 }
                 this.keycloak.logout(redirectUri);
             })        
@@ -61,13 +61,14 @@ export class AuthEffects {
     openEditProfile$ =  createEffect(() =>
         this.actions$.pipe(
             ofType(authActions.openEditProfile),
-            tap(_ => window.open(environment.ssoAuthUrl + '/realms/' + environment.ssoRealm + '/account', '_blank'))        
+            tap(_ => window.open(this.config.ssoAuthUrl + '/realms/' + this.config.ssoRealm + '/account', '_blank'))        
         ),
         { dispatch: false }
     );
  
     constructor(
         private actions$: Actions,
-        private keycloak: KeycloakService
+        private keycloak: KeycloakService,
+        private config: AppConfigService
     ) {}
 }
diff --git a/client/src/app/auth/auth.module.ts b/client/src/app/auth/auth.module.ts
index 1c04b526d5fa4866c40d04c2ecc6ccaae2284cfb..5f92830201a13e8f8b84300b2cab0cd4cd0989cb 100644
--- a/client/src/app/auth/auth.module.ts
+++ b/client/src/app/auth/auth.module.ts
@@ -13,19 +13,14 @@ import { KeycloakAngularModule } from 'keycloak-angular';
 import { StoreModule } from '@ngrx/store';
 import { EffectsModule } from '@ngrx/effects';
 
-import { initializeKeycloakAnis } from './init.keycloak';
 import { authReducer } from './auth.reducer';
 import { AuthEffects } from './auth.effects';
-import { environment } from 'src/environments/environment';
 
 @NgModule({
     imports: [
-        environment.authenticationEnabled ? KeycloakAngularModule : [],
+        KeycloakAngularModule,
         StoreModule.forFeature('auth', authReducer),
-        environment.authenticationEnabled ? EffectsModule.forFeature([ AuthEffects ]): []
-    ],
-    providers: [
-        environment.authenticationEnabled ? initializeKeycloakAnis: []
+        EffectsModule.forFeature([ AuthEffects ])
     ]
 })
 export class AuthModule { }
diff --git a/client/src/app/auth/init.keycloak.ts b/client/src/app/auth/init.keycloak.ts
index f0509a317a70c330bc5a31107aa51cc3c2f404cd..de6c1bc3a350aa3656e1986be4a10874269d4b44 100644
--- a/client/src/app/auth/init.keycloak.ts
+++ b/client/src/app/auth/init.keycloak.ts
@@ -7,48 +7,41 @@
  * file that was distributed with this source code.
  */
 
-import { APP_INITIALIZER } from '@angular/core';
 import { from } from 'rxjs';
 
 import { KeycloakService, KeycloakEventType } from 'keycloak-angular';
 import { Store } from '@ngrx/store';
 
 import * as keycloakActions from './auth.actions';
-import * as fromKeycloak from './auth.reducer';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from '../app-config.service';
 
-function initializeKeycloak(keycloak: KeycloakService, store: Store<{ keycloak: fromKeycloak.State }>) {
-    return async () => {
-        from(keycloak.keycloakEvents$).subscribe(event => {
-            if (event.type === KeycloakEventType.OnAuthSuccess) {
-                store.dispatch(keycloakActions.authSuccess());
-            }
-        })
+export function initializeKeycloak(keycloak: KeycloakService, store: Store<{ }>, appConfigService: AppConfigService) {
+    if (!appConfigService.authenticationEnabled) {
+        return true;
+    }
 
-        let silentCheckSsoRedirectUri = window.location.origin;
-        if (environment.baseHref != '/') {
-            silentCheckSsoRedirectUri += environment.baseHref;
+    from(keycloak.keycloakEvents$).subscribe(event => {
+        if (event.type === KeycloakEventType.OnAuthSuccess) {
+            store.dispatch(keycloakActions.authSuccess());
         }
-        silentCheckSsoRedirectUri += '/assets/silent-check-sso.html';
+    })
 
-        return keycloak.init({
-            config: {
-                url: environment.ssoAuthUrl,
-                realm: environment.ssoRealm,
-                clientId: environment.ssoClientId,
-            },
-            initOptions: {
-                onLoad: 'check-sso',
-                silentCheckSsoRedirectUri
-            },
-            loadUserProfileAtStartUp: true
-        });
+    let silentCheckSsoRedirectUri = window.location.origin;
+    if (appConfigService.baseHref != '/') {
+        silentCheckSsoRedirectUri += appConfigService.baseHref;
     }
-}
+    silentCheckSsoRedirectUri += '/assets/silent-check-sso.html';
 
-export const initializeKeycloakAnis = {
-    provide: APP_INITIALIZER,
-    useFactory: initializeKeycloak,
-    multi: true,
-    deps: [ KeycloakService, Store ],
-};
+    return keycloak.init({
+        config: {
+            url: appConfigService.ssoAuthUrl,
+            realm: appConfigService.ssoRealm,
+            clientId: appConfigService.ssoClientId,
+        },
+        initOptions: {
+            onLoad: 'check-sso',
+            silentCheckSsoRedirectUri
+        },
+        loadUserProfileAtStartUp: true
+    });
+}
diff --git a/client/src/app/core/containers/app.component.ts b/client/src/app/core/containers/app.component.ts
index c265196bb2531b78a0262cc364ced47b63bd68f7..0de038c07d634c0c15eb078183af7b47c4706122 100644
--- a/client/src/app/core/containers/app.component.ts
+++ b/client/src/app/core/containers/app.component.ts
@@ -17,7 +17,7 @@ import * as fromAuth from '../../auth/auth.reducer';
 import * as authActions from '../../auth/auth.actions';
 import * as authSelector from '../../auth/auth.selector';
 import { UserProfile } from '../../auth/user-profile.model';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
   selector: 'app-root',
@@ -30,14 +30,14 @@ export class AppComponent {
     public userProfile: Observable<UserProfile>;
     public userRoles: Observable<string[]>;
 
-    constructor(private store: Store<{ auth: fromAuth.State }>) {
+    constructor(private store: Store<{ auth: fromAuth.State }>, private config: AppConfigService) {
         this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
         this.userProfile = store.select(authSelector.selectUserProfile);
         this.userRoles = store.select(authSelector.selectUserRoles);
     }
 
     authenticationEnabled(): boolean {
-        return environment.authenticationEnabled;
+        return this.config.authenticationEnabled;
     }
 
     login(): void {
diff --git a/client/src/app/instance/detail/components/object-data.component.ts b/client/src/app/instance/detail/components/object-data.component.ts
index d834874bede64081c6f7b45b6c5f4f0e5b9a1ad9..16de054ffd8348a0701382356e74f9412523540e 100644
--- a/client/src/app/instance/detail/components/object-data.component.ts
+++ b/client/src/app/instance/detail/components/object-data.component.ts
@@ -11,6 +11,7 @@ import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 
 import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
 import { getHost } from 'src/app/shared/utils';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-object-data',
@@ -28,6 +29,8 @@ export class ObjectDataComponent {
     @Input() attributeList: Attribute[];
     @Input() object: any;
 
+    constructor(private appConfig: AppConfigService) { }
+
     /**
      * Returns category list sorted by display, for the given output family ID.
      *
@@ -78,6 +81,6 @@ export class ObjectDataComponent {
     }
 
     getDownloadHref(value: string) {
-        return getHost() + '/download-file/' + this.datasetSelected + '/' + value;
+        return getHost(this.appConfig.apiUrl) + '/download-file/' + this.datasetSelected + '/' + value;
     }
 }
diff --git a/client/src/app/instance/detail/components/spectra/spectra-object.component.ts b/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
index df397e7ea50f131d916162665963e277f9401229..9c7095f2860c9712915d6dc5c61e0676fe7fc73d 100644
--- a/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
+++ b/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
@@ -11,6 +11,7 @@ import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter, OnInit
 
 import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
 import { getHost } from 'src/app/shared/utils';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-spectra-object',
@@ -35,6 +36,8 @@ export class SpectraObjectComponent implements OnInit {
     @Input() spectraCSV: string;
     @Output() getSpectraCSV: EventEmitter<string> = new EventEmitter();
 
+    constructor(private appConfig: AppConfigService) { }
+
     ngOnInit() {
         const attributeSpectraGraph = this.getAttributeSpectraGraph();
         if (attributeSpectraGraph) {
@@ -51,7 +54,7 @@ export class SpectraObjectComponent implements OnInit {
         const spectraAttribute: Attribute = this.attributeList
             .filter(a => a.detail)
             .find(attribute => attribute.search_flag === 'SPECTRUM_1D');
-        return getHost() + '/download-file/' + this.datasetSelected + '/' + this.object[spectraAttribute.label];
+        return getHost(this.appConfig.apiUrl) + '/download-file/' + this.datasetSelected + '/' + this.object[spectraAttribute.label];
     }
 
     /**
diff --git a/client/src/app/instance/documentation/documentation.component.ts b/client/src/app/instance/documentation/documentation.component.ts
index 7a41485040b072354f6df74b0becf5ed80104106..ef1e591e1cf2cc8e9387046451201a2352d697d0 100644
--- a/client/src/app/instance/documentation/documentation.component.ts
+++ b/client/src/app/instance/documentation/documentation.component.ts
@@ -15,7 +15,7 @@ import { Observable } from 'rxjs';
 import * as datasetActions from 'src/app/metamodel/actions/dataset.actions';
 import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
 import { Dataset } from 'src/app/metamodel/models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-documentation',
@@ -33,7 +33,7 @@ export class DocumentationComponent implements OnInit {
     public datasetListIsLoaded: Observable<boolean>;
     public datasetList: Observable<Dataset[]>;
 
-    constructor(private store: Store<{ }>) {
+    constructor(private store: Store<{ }>, private config: AppConfigService) {
         this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading);
         this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded);
         this.datasetList = store.select(datasetSelector.selectAllDatasets);
@@ -49,10 +49,10 @@ export class DocumentationComponent implements OnInit {
      * @return string
      */
     getUrlServer(): string {
-        if (!environment.apiUrl.startsWith('http')) {
+        if (!this.config.apiUrl.startsWith('http')) {
             const url = window.location;
-            return url.protocol + '//' + url.host + environment.apiUrl;
+            return url.protocol + '//' + url.host + this.config.apiUrl;
         }
-        return environment.apiUrl;
+        return this.config.apiUrl;
     }
 }
diff --git a/client/src/app/instance/instance.component.html b/client/src/app/instance/instance.component.html
index 979df50e47db62138793be21dff9adf7df5e4cf1..61101934f1e33d3d27823eb8998cedcf423d891b 100644
--- a/client/src/app/instance/instance.component.html
+++ b/client/src/app/instance/instance.component.html
@@ -3,6 +3,8 @@
         [links]="links"
         [isAuthenticated]="isAuthenticated | async"
         [userProfile]="userProfile | async"
+        [baseHref]="getBaseHref()"
+        [authenticationEnabled]="getAuthenticationEnabled()"
         (login)="login()"
         (logout)="logout()"
         (openEditProfile)="openEditProfile()">
diff --git a/client/src/app/instance/instance.component.ts b/client/src/app/instance/instance.component.ts
index 6d2d31467394751078d5b9c14dd36ac4b0fee2bd..53fc798c6a9f63911e6127d4a58bec1aad4b4607 100644
--- a/client/src/app/instance/instance.component.ts
+++ b/client/src/app/instance/instance.component.ts
@@ -15,6 +15,7 @@ import { UserProfile } from 'src/app/auth/user-profile.model';
 import * as authActions from 'src/app/auth/auth.actions';
 import * as authSelector from 'src/app/auth/auth.selector';
 import * as metamodelActions from './store/actions/metamodel.actions';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-instance',
@@ -35,7 +36,7 @@ export class InstanceComponent implements OnInit {
     public userProfile: Observable<UserProfile>;
     public userRoles: Observable<string[]>;
 
-    constructor(private store: Store<{ }>) {
+    constructor(private store: Store<{ }>, private config: AppConfigService) {
         this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
         this.userProfile = store.select(authSelector.selectUserProfile);
         this.userRoles = store.select(authSelector.selectUserRoles);
@@ -46,6 +47,14 @@ export class InstanceComponent implements OnInit {
         Promise.resolve(null).then(() => this.store.dispatch(metamodelActions.loadInstanceMetamodel()));
     }
 
+    getBaseHref() {
+        return this.config.baseHref;
+    }
+
+    getAuthenticationEnabled() {
+        return this.config.authenticationEnabled;
+    }
+
     login(): void {
         this.store.dispatch(authActions.login());
     }
diff --git a/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts
deleted file mode 100644
index 72ebd410aa82ef81bf7c10fd618ab211ad3c581d..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
-
-import { BetweenDateComponent } from './between-date.component';
-import { BetweenCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: BetweenDateComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-between-date [criterion]='criterion'></app-between-date>`
-    })
-    class TestHostComponent {
-        @ViewChild(BetweenDateComponent, { static: false })
-        public testedComponent: BetweenDateComponent;
-        public criterion: BetweenCriterion = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: BetweenDateComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [BetweenDateComponent, TestHostComponent],
-            imports: [BsDatepickerModule.forRoot(), FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDateString() should format a datetime object into a string', () => {
-        const dateObject = new Date('February 08, 2019 15:47:00');
-        const expectedDateString = '2019-02-08';
-        const formatedDate = testedComponent.getDateString(dateObject);
-        expect(formatedDate).toEqual(expectedDateString);
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.field.value).toBeNull();
-        expect(testedComponent.field.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const criterion = { id: 1, type: 'between', min: '2019-02-08', max: '2019-02-17' } as BetweenCriterion;
-        const expectedFieldValue = [new Date(criterion.min), new Date(criterion.max)];
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.field.value).toEqual(expectedFieldValue);
-        expect(testedComponent.field.disabled).toBeTruthy();
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const dateMin = '2019-02-08';
-        const dateMax = '2019-02-17';
-        testedComponent.field = new FormControl([new Date(dateMin), new Date(dateMax)]);
-        const expectedCriterion = { id: testedComponent.id, type: 'between', min: dateMin, max: dateMax } as BetweenCriterion;
-        testedComponent.addCriterion.subscribe((event: BetweenCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts
deleted file mode 100644
index 80df0b7b01ffcf564995ab3e582e1cadc754b4b0..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { BetweenComponent } from './between.component';
-import { BetweenCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: BetweenComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-between [criterion]='criterion'></app-between>`
-    })
-    class TestHostComponent {
-        @ViewChild(BetweenComponent, { static: false })
-        public testedComponent: BetweenComponent;
-        public criterion: BetweenCriterion = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: BetweenComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [BetweenComponent, TestHostComponent],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.fieldMin.value).toBeNull();
-        expect(testedComponent.fieldMin.enabled).toBeTruthy();
-        expect(testedComponent.fieldMax.value).toBeNull();
-        expect(testedComponent.fieldMax.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const min = '10';
-        const max = '20';
-        const criterion = { id: 1, type: 'between', min, max } as BetweenCriterion;
-        const expectedFieldMinValue = min;
-        const expectedFieldMaxValue = max;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.fieldMin.value).toEqual(expectedFieldMinValue);
-        expect(testedComponent.fieldMin.disabled).toBeTruthy();
-        expect(testedComponent.fieldMax.value).toEqual(expectedFieldMaxValue);
-        expect(testedComponent.fieldMax.disabled).toBeTruthy();
-    });
-
-    it('#getPlaceholderMin() should fill the placeholder if defined', () => {
-        const placeholder = '10';
-        testedComponent.placeholderMin = placeholder;
-        expect(testedComponent.getPlaceholderMin()).toEqual(placeholder);
-    });
-
-    it('#getPlaceholderMin() should not fill the placeholder if not defined', () => {
-        expect(testedComponent.getPlaceholderMin()).toEqual('');
-    });
-
-    it('#getPlaceholderMax() should fill the placeholder if defined', () => {
-        const placeholder = '10';
-        testedComponent.placeholderMax = placeholder;
-        expect(testedComponent.getPlaceholderMax()).toEqual(placeholder);
-    });
-
-    it('#getPlaceholderMax() should not fill the placeholder if not defined', () => {
-        expect(testedComponent.getPlaceholderMax()).toEqual('');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const min = '10';
-        const max = '20';
-        testedComponent.fieldMin = new FormControl(min);
-        testedComponent.fieldMax = new FormControl(max);
-        const expectedCriterion = { id: testedComponent.id, type: 'between', min, max } as BetweenCriterion;
-        testedComponent.addCriterion.subscribe((event: BetweenCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts
deleted file mode 100644
index 0c10c5ec9edd0c2d1e345cc00cb52b6b65a4efe8..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl, FormArray } from '@angular/forms';
-
-import { CheckboxComponent } from './checkbox.component';
-import { SelectMultipleCriterion } from '../../../store/model';
-import { Option } from '../../../../metamodel/model';
-
-describe('[Search][Criteria][SearchType] Component: CheckboxComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-checkbox [options]='options' [criterion]='criterion'></app-checkbox>`
-    })
-    class TestHostComponent {
-        @ViewChild(CheckboxComponent, { static: false })
-        public testedComponent: CheckboxComponent;
-        public criterion: SelectMultipleCriterion = undefined;
-        public options: Option[] = [
-            { label: 'One', value: 'one', display: 1 },
-            { label: 'Two', value: 'two', display: 2 },
-            { label: 'Three', value: 'three', display: 3 }
-        ];
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: CheckboxComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [CheckboxComponent, TestHostComponent],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should not fill and enable form if criterion not defined', () => {
-        expect(testedComponent.checkboxes.length).toEqual(3);
-        expect(testedComponent.isChecked()).toBeFalsy();
-        expect(testedComponent.checkboxes.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const options: Option[] = [
-            { label: 'One', value: 'one', display: 1 },
-            { label: 'Two', value: 'two', display: 2 }
-        ];
-        const criterion = { id: 1, type: 'multiple', options} as SelectMultipleCriterion;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.isChecked()).toBeTruthy();
-        const values = testedComponent.checkboxes.value as boolean[];
-        expect(values.filter(v => v).length).toEqual(2);
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.checkboxes = new FormArray([
-            new FormControl(false),
-            new FormControl(true),
-            new FormControl(true)
-        ])
-        const expectedCriterion = {
-            id: 1,
-            type: 'multiple',
-            options: [
-                { label: 'Two', value: 'two', display: 2 },
-                { label: 'Three', value: 'three', display: 3 }
-            ]
-        };
-        testedComponent.addCriterion.subscribe((event: SelectMultipleCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts
deleted file mode 100644
index 588ac8c5304030b4386af241390533238f912512..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { DatalistComponent } from './datalist.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: DatalistComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-datalist [criterion]='criterion'></app-datalist>`
-    })
-    class TestHostComponent {
-        @ViewChild(DatalistComponent, { static: false })
-        public testedComponent: DatalistComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    @Component({ selector: 'app-operator', template: '' })
-    class OperatorStubComponent {
-        @Input() operator: string;
-        @Input() searchType: string;
-        @Input() advancedForm: boolean;
-        @Input() disabled: boolean;
-        @Output() changeOperator: EventEmitter<string> = new EventEmitter();
-    }
-
-    @Component({ selector: 'app-help-like', template: '' })
-    class HelpLikeStubComponent { }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: DatalistComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                DatalistComponent,
-                TestHostComponent,
-                OperatorStubComponent,
-                HelpLikeStubComponent
-            ],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.field.value).toBeNull();
-        expect(testedComponent.field.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const value = '10';
-        const criterion = { id: 1, type: 'field', operator: 'eq', value } as FieldCriterion;
-        const expectedFieldValue = value;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.field.value).toEqual(expectedFieldValue);
-        expect(testedComponent.field.disabled).toBeTruthy();
-    });
-
-    it('#getPlaceholder() should fill the placeholder if defined', () => {
-        const placeholder = '10';
-        testedComponent.placeholder = placeholder;
-        expect(testedComponent.getPlaceholder()).toEqual(placeholder);
-    });
-
-    it('#getPlaceholder() should not fill the placeholder if not defined', () => {
-        expect(testedComponent.getPlaceholder()).toEqual('');
-    });
-
-    it('#getDatalistId() should return an id', () => {
-        testedComponent.id = 1;
-        expect(testedComponent.getDatalistId()).toEqual('datalist_' + 1);
-    });
-
-    it('#changeOperator() should change the operator', () => {
-        expect(testedComponent.operator).toBeUndefined();
-        testedComponent.changeOperator('toto');
-        expect(testedComponent.operator).toBe('toto');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = '=';
-        const value = '10';
-        testedComponent.field = new FormControl(value);
-        testedComponent.operator = operator;
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts
deleted file mode 100644
index a67b65f674076ee75448da86877a1bdc093317f2..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
-
-import { DateComponent } from './date.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: DateComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-date [criterion]='criterion'></app-date>`
-    })
-    class TestHostComponent {
-        @ViewChild(DateComponent, { static: false })
-        public testedComponent: DateComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    @Component({ selector: 'app-operator', template: '' })
-    class OperatorStubComponent {
-        @Input() operator: string;
-        @Input() searchType: string;
-        @Input() advancedForm: boolean;
-        @Input() disabled: boolean;
-        @Output() changeOperator: EventEmitter<string> = new EventEmitter();
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: DateComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                DateComponent, 
-                TestHostComponent,
-                OperatorStubComponent
-            ],
-            imports: [BsDatepickerModule.forRoot(), FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.field.value).toBeNull();
-        expect(testedComponent.field.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const value = '2019-02-17';
-        const criterion = { id: 1, type: 'field', operator: 'eq', value } as FieldCriterion;
-        const expectedFieldValue = new Date(value);
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.field.value).toEqual(expectedFieldValue);
-        expect(testedComponent.field.disabled).toBeTruthy();
-    });
-
-    it('#getPlaceholder() should fill the placeholder if defined', () => {
-        const placeholder = '2019-02-17';
-        testedComponent.placeholder = placeholder;
-        expect(testedComponent.getPlaceholder()).toEqual(placeholder);
-    });
-
-    it('#getPlaceholder() should not fill the placeholder if not defined', () => {
-        expect(testedComponent.getPlaceholder()).toEqual('');
-    });
-
-    it('#getDateString() should return a date as string', () => {
-        const dateString = '2019-02-17';
-        const date = new Date(dateString);
-        expect(testedComponent.getDateString(date)).toEqual(dateString);
-    });
-
-    it('#changeOperator() should change the operator', () => {
-        expect(testedComponent.operator).toBeUndefined();
-        testedComponent.changeOperator('toto');
-        expect(testedComponent.operator).toBe('toto');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = '=';
-        testedComponent.operator = operator;
-        const date = '2019-02-17';
-        testedComponent.field = new FormControl(new Date(date));
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value: date } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts
deleted file mode 100644
index 045aeeeeee9bdf4ea743d8308504869ba35e73b3..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
-import { NgSelectModule } from '@ng-select/ng-select';
-
-import { DatetimeComponent } from './datetime.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: DatetimeComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-datetime [criterion]='criterion'></app-datetime>`
-    })
-    class TestHostComponent {
-        @ViewChild(DatetimeComponent, { static: false })
-        public testedComponent: DatetimeComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    @Component({ selector: 'app-operator', template: '' })
-    class OperatorStubComponent {
-        @Input() operator: string;
-        @Input() searchType: string;
-        @Input() advancedForm: boolean;
-        @Input() disabled: boolean;
-        @Output() changeOperator: EventEmitter<string> = new EventEmitter();
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: DatetimeComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                DatetimeComponent,
-                TestHostComponent,
-                OperatorStubComponent
-            ],
-            imports: [BsDatepickerModule.forRoot(), NgSelectModule, FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.date.value).toBeNull();
-        expect(testedComponent.date.enabled).toBeTruthy();
-        expect(testedComponent.hh.value).toBeNull();
-        expect(testedComponent.hh.enabled).toBeTruthy();
-        expect(testedComponent.mm.value).toBeNull();
-        expect(testedComponent.mm.enabled).toBeTruthy();
-        expect(testedComponent.isValidFields).toBeFalsy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const value = '2019-02-17 15:47';
-        const criterion = { id: 1, type: 'field', operator: 'eq', value } as FieldCriterion;
-        const expectedDate = new Date('2019-02-17');
-        const expectedHour = '15';
-        const expectedMinute = '47';
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.date.value).toEqual(expectedDate);
-        expect(testedComponent.date.disabled).toBeTruthy();
-        expect(testedComponent.hh.value).toEqual(expectedHour);
-        expect(testedComponent.hh.disabled).toBeTruthy();
-        expect(testedComponent.mm.value).toEqual(expectedMinute);
-        expect(testedComponent.mm.disabled).toBeTruthy();
-        expect(testedComponent.isValidFields).toBeTruthy();
-    });
-
-    it('#initTime(t) should return an array of string with 2 digits from 0 to t', () => {
-        const n = 10;
-        expect(testedComponent.initTime(n).length).toEqual(n);
-        expect(testedComponent.initTime(n)[5]).toEqual('05');
-    });
-
-    it('#change() should set #isValidFields to false if one or more fields are not defined', () => {
-        testedComponent.change();
-        expect(testedComponent.isValidFields).toBeFalsy();
-        testedComponent.hh = new FormControl('15');
-        testedComponent.change();
-        expect(testedComponent.isValidFields).toBeFalsy();
-        testedComponent.mm = new FormControl('47');
-        testedComponent.change();
-        expect(testedComponent.isValidFields).toBeFalsy();
-        testedComponent.date = new FormControl(new Date('2019-02-17'));
-        testedComponent.change();
-        expect(testedComponent.isValidFields).toBeTruthy();
-    });
-
-    it('#change() should set #datetime with fields value when defined', () => {
-        testedComponent.date = new FormControl(new Date('2019-02-17'));
-        testedComponent.hh = new FormControl('15');
-        testedComponent.mm = new FormControl('47');
-        testedComponent.change();
-        expect(testedComponent.datetime).toEqual(new Date('2019-02-17 15:47'));
-    });
-
-    it('#changeOperator() should change the operator', () => {
-        expect(testedComponent.operator).toBeUndefined();
-        testedComponent.changeOperator('toto');
-        expect(testedComponent.operator).toBe('toto');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = '=';
-        testedComponent.operator = operator;
-        testedComponent.datetime = new Date('2019-02-17 15:47');
-        testedComponent.hh = new FormControl('15');
-        testedComponent.mm = new FormControl('47');
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value: '2019-02-17 15:47' } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/field.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/field.component.spec.ts
deleted file mode 100644
index 4c7d55122bb30cd3b326b0de96acaf49da61cc6a..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/field.component.spec.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { FieldComponent } from './field.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: FieldComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-field [criterion]='criterion'></app-field>`
-    })
-    class TestHostComponent {
-        @ViewChild(FieldComponent, { static: false })
-        public testedComponent: FieldComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    @Component({ selector: 'app-operator', template: '' })
-    class OperatorStubComponent {
-        @Input() operator: string;
-        @Input() searchType: string;
-        @Input() advancedForm: boolean;
-        @Input() disabled: boolean;
-        @Output() changeOperator: EventEmitter<string> = new EventEmitter();
-    }
-
-    @Component({ selector: 'app-help-like', template: '' })
-    class HelpLikeStubComponent { }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: FieldComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                FieldComponent,
-                TestHostComponent,
-                OperatorStubComponent,
-                HelpLikeStubComponent
-            ],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.field.value).toBeNull();
-        expect(testedComponent.field.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const value = 'test';
-        const criterion = { id: 1, type: 'field', operator: 'eq', value } as FieldCriterion;
-        const expectedFieldValue = value;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.field.value).toEqual(expectedFieldValue);
-        expect(testedComponent.field.disabled).toBeTruthy();
-    });
-
-    it('#getType() should return `number` if criterion is a number type', () => {
-        testedComponent.attributeType = 'smallint';
-        expect(testedComponent.getType()).toEqual('number');
-        testedComponent.attributeType = 'integer';
-        expect(testedComponent.getType()).toEqual('number');
-        testedComponent.attributeType = 'decimal';
-        expect(testedComponent.getType()).toEqual('number');
-        testedComponent.attributeType = 'float';
-        expect(testedComponent.getType()).toEqual('number');
-    });
-
-    it('#getType() should return `text` if criterion is not a number type', () => {
-        testedComponent.attributeType = 'char';
-        expect(testedComponent.getType()).toEqual('text');
-    });
-
-    it('#getPlaceholder() should fill the placeholder if defined', () => {
-        const placeholder = 'placeholder';
-        testedComponent.placeholder = placeholder;
-        expect(testedComponent.getPlaceholder()).toEqual(placeholder);
-    });
-
-    it('#getPlaceholder() should not fill the placeholder if not defined', () => {
-        expect(testedComponent.getPlaceholder()).toEqual('');
-    });
-
-    it('#changeOperator() should change the operator', () => {
-        expect(testedComponent.operator).toBeUndefined();
-        testedComponent.changeOperator('toto');
-        expect(testedComponent.operator).toBe('toto');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = '=';
-        testedComponent.operator = operator;
-        const value = 'test';
-        testedComponent.field = new FormControl(value);
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/help-like.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/help-like.component.spec.ts
deleted file mode 100644
index 62a5a61175432e293174d02ce10b91c589b5a62f..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/help-like.component.spec.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { HelpLikeComponent } from './help-like.component';
-
-describe('[Search][Criteria][SearchType] Component: HelpLikeComponent', () => {
-    let component: HelpLikeComponent;
-    let fixture: ComponentFixture<HelpLikeComponent>;
-
-    beforeEach(() => {
-        TestBed.configureTestingModule({
-            declarations: [HelpLikeComponent]
-        });
-        fixture = TestBed.createComponent(HelpLikeComponent);
-        component = fixture.componentInstance;
-    });
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
deleted file mode 100644
index 29767ff55c53ba7454cfa1e90a38f401fc4ba24b..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-
-import { JsonComponent } from './json.component';
-import { JsonCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: JsonComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-json-criteria [criterion]='criterion'></app-json-criteria>`
-    })
-    class TestHostComponent {
-        @ViewChild(JsonComponent, { static: false })
-        public testedComponent: JsonComponent;
-        public criterion: JsonCriterion = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: JsonComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [JsonComponent, TestHostComponent],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.jsonForm.controls.path.value).toBeNull();
-        expect(testedComponent.jsonForm.controls.operator.value).toBeNull();
-        expect(testedComponent.jsonForm.controls.value.value).toBeNull();
-        expect(testedComponent.jsonForm.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const path = 'path';
-        const operator = '=';
-        const value = 'test';
-        const criterion = { id: 1, type: 'json', path, operator, value } as JsonCriterion;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.jsonForm.controls.path.value).toEqual(path);
-        expect(testedComponent.jsonForm.controls.operator.value).toEqual(operator);
-        expect(testedComponent.jsonForm.controls.value.value).toEqual(value);
-        expect(testedComponent.jsonForm.disabled).toBeTruthy();
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const path = 'path';
-        const operator = '=';
-        const value = 'test';
-        testedComponent.jsonForm.controls.path.setValue(path);
-        testedComponent.jsonForm.controls.operator.setValue(operator);
-        testedComponent.jsonForm.controls.value.setValue(value);
-        const expectedCriterion = { id: testedComponent.id, type: 'json', path, operator, value } as JsonCriterion;
-        testedComponent.addCriterion.subscribe((event: JsonCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/list.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/list.component.spec.ts
deleted file mode 100644
index d08f64b79485959e4474efe49f989faad0b23ca3..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/list.component.spec.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { ListComponent } from './list.component';
-import { ListCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: ListComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-list [criterion]='criterion'></app-list>`
-    })
-    class TestHostComponent {
-        @ViewChild(ListComponent, { static: false })
-        public testedComponent: ListComponent;
-        public criterion: ListComponent = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: ListComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                ListComponent,
-                TestHostComponent
-            ],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.list.value).toBeNull();
-        expect(testedComponent.list.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const values = ['1', '2'];
-        const criterion = { id: 1, type: 'list', values } as ListCriterion;
-        const expectedListValues = values.join('\n');
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.list.value).toBe(expectedListValues);
-        expect(testedComponent.list.disabled).toBeTruthy();
-    });
-
-    it('#getPlaceholder() should fill the placeholder if defined', () => {
-        const placeholder = 'placeholder';
-        testedComponent.placeholder = placeholder;
-        expect(testedComponent.getPlaceholder()).toBe(placeholder);
-    });
-
-    it('#getPlaceholder() should not fill the placeholder if not defined', () => {
-        expect(testedComponent.getPlaceholder()).toBe('');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const values = '1\n2';
-        testedComponent.list = new FormControl(values);
-        const expectedCriterion = { id: testedComponent.id, type: 'list', values: ['1', '2'] } as ListCriterion;
-        testedComponent.addCriterion.subscribe((event: ListCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/operator.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/operator.component.spec.ts
deleted file mode 100644
index cec4db0981a0806e7dd6112bea0e1c8e75cc3b7b..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/operator.component.spec.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-
-import { OperatorComponent } from './operator.component';
-
-describe('[Search][Criteria][SearchType] Component: OperatorComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `
-            <app-operator
-                [operator]='operator'
-                [searchType]='searchType'
-                [advancedForm]='advancedForm'
-                [disabled]='disabled'>
-            </app-operator>`
-    })
-    class TestHostComponent {
-        @ViewChild(OperatorComponent, { static: false })
-        testedComponent: OperatorComponent;
-        operator = 'eq';
-        searchType: string = undefined;
-        advancedForm: boolean = undefined;
-        disabled: boolean = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: OperatorComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [OperatorComponent, TestHostComponent],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getLabel() should return the correct operator form label', () => {
-        expect(testedComponent.getLabel('eq')).toBe('=');
-        expect(testedComponent.getLabel('neq')).toBe('≠');
-        expect(testedComponent.getLabel('gt')).toBe('>');
-        expect(testedComponent.getLabel('gte')).toBe('>=');
-        expect(testedComponent.getLabel('lt')).toBe('<');
-        expect(testedComponent.getLabel('lte')).toBe('<=');
-        expect(testedComponent.getLabel('lk')).toBe('like');
-        expect(testedComponent.getLabel('nlk')).toBe('not like');
-    });
-
-    it('raises the changeOperator event when the value change', () => {
-        testedComponent.changeOperator.subscribe((event: string) => expect(event).toEqual('eq'));
-        testedComponent.emitChange('eq');
-    });
-
-    it('should display the select box when it\'s an enabled advanced form', () => {
-        testHostComponent.advancedForm = true;
-        testHostComponent.disabled = false;
-        testHostFixture.detectChanges();
-        const template = testHostFixture.nativeElement;
-        expect(template.querySelector('select')).toBeTruthy();
-        expect(template.querySelector('.readonly')).toBeFalsy();
-    });
-
-    it('should display the readonly field when it\'s not an advanced form or not enabled advanced form', () => {
-        testHostComponent.advancedForm = true;
-        testHostComponent.disabled = true;
-        testHostFixture.detectChanges();
-        const template = testHostFixture.nativeElement;
-        expect(template.querySelector('select')).toBeFalsy();
-        expect(template.querySelector('.readonly')).toBeTruthy();
-        testHostComponent.advancedForm = true;
-        testHostFixture.detectChanges();
-        expect(template.querySelector('select')).toBeFalsy();
-        expect(template.querySelector('.readonly')).toBeTruthy();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/radio.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/radio.component.spec.ts
deleted file mode 100644
index e6b5658f4821e904e6942dd10bbc921450524d73..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/radio.component.spec.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { Component, ViewChild } from '@angular/core';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { RadioComponent } from './radio.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: RadioComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-radio [criterion]='criterion'></app-radio>`
-    })
-    class TestHostComponent {
-        @ViewChild(RadioComponent, { static: false })
-        public testedComponent: RadioComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: RadioComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [RadioComponent, TestHostComponent],
-            imports: [FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.radio.value).toBeNull();
-        expect(testedComponent.radio.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const operator = '=';
-        const value = 'test';
-        const criterion = { id: 1, type: 'field', operator, value } as FieldCriterion;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.radio.value).toEqual(value);
-        expect(testedComponent.radio.disabled).toBeTruthy();
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = '=';
-        testedComponent.operator = operator;
-        const value = 'three';
-        testedComponent.radio = new FormControl(value);
-        testedComponent.options = [
-            { label: 'One', value: 'one', display: 1 },
-            { label: 'Two', value: 'two', display: 2 },
-            { label: 'Three', value: 'three', display: 3 }
-        ];
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts
deleted file mode 100644
index e6f8ccfd504802a0a1b8031e59e4dc4a86326dc1..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { Component, ViewChild } from '@angular/core';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { NgSelectModule } from '@ng-select/ng-select';
-
-import { SelectMultipleComponent } from './select-multiple.component';
-import { SelectMultipleCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: SelectMultipleComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-select-multiple [criterion]='criterion'></app-select-multiple>`
-    })
-    class TestHostComponent {
-        @ViewChild(SelectMultipleComponent, { static: false })
-        public testedComponent: SelectMultipleComponent;
-        public criterion: SelectMultipleCriterion = undefined;
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: SelectMultipleComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [SelectMultipleComponent, TestHostComponent],
-            imports: [NgSelectModule, FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.ms.value).toBeNull();
-        expect(testedComponent.ms.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const options = [
-            { label: 'One', value: 'one', display: 1 },
-            { label: 'Two', value: 'two', display: 2 },
-            { label: 'Three', value: 'three', display: 3 }
-        ];
-        const criterion = { id: 1, type: 'multiple', options} as SelectMultipleCriterion;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.ms.value).toEqual(['one', 'two', 'three']);
-        expect(testedComponent.ms.disabled).toBeTruthy();
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.options = [
-            { label: 'One', value: 'one', display: 1 },
-            { label: 'Two', value: 'two', display: 2 },
-            { label: 'Three', value: 'three', display: 3 }
-        ];
-        const value = ['three'];
-        testedComponent.ms = new FormControl(value);
-        const expectedValue = [
-            { label: 'Three', value: 'three', display: 3 }
-        ];
-        const expectedCriterion = { id: testedComponent.id, type: 'multiple', options: expectedValue } as SelectMultipleCriterion;
-        testedComponent.addCriterion.subscribe((event: SelectMultipleCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/select.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/select.component.spec.ts
deleted file mode 100644
index 05f8edff0f509d817f66706bf62fe2fa7ae28bc7..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/select.component.spec.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { NgSelectModule } from '@ng-select/ng-select';
-
-import { SelectComponent } from './select.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: SelectComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-select [criterion]='criterion'></app-select>`
-    })
-    class TestHostComponent {
-        @ViewChild(SelectComponent, { static: false })
-        public testedComponent: SelectComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    @Component({ selector: 'app-operator', template: '' })
-    class OperatorStubComponent {
-        @Input() operator: string;
-        @Input() searchType: string;
-        @Input() advancedForm: boolean;
-        @Input() disabled: boolean;
-        @Output() changeOperator: EventEmitter<string> = new EventEmitter();
-    }
-
-    @Component({ selector: 'app-help-like', template: '' })
-    class HelpLikeStubComponent { }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: SelectComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                SelectComponent, 
-                TestHostComponent,
-                OperatorStubComponent,
-                HelpLikeStubComponent
-            ],
-            imports: [NgSelectModule, FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.se.value).toBeNull();
-        expect(testedComponent.se.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const operator = '=';
-        const value = 'test';
-        const criterion = { id: 1, type: 'field', operator, value } as FieldCriterion;
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.se.value).toEqual(value);
-        expect(testedComponent.se.disabled).toBeTruthy();
-    });
-
-    it('#changeOperator() should change the operator', () => {
-        expect(testedComponent.operator).toBeUndefined();
-        testedComponent.changeOperator('toto');
-        expect(testedComponent.operator).toBe('toto');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = '=';
-        testedComponent.operator = operator;
-        const value = 'three';
-        testedComponent.se = new FormControl(value);
-        testedComponent.options = [
-            { label: 'One', value: 'one', display: 1 },
-            { label: 'Two', value: 'two', display: 2 },
-            { label: 'Three', value: 'three', display: 3 }
-        ];
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts
deleted file mode 100644
index f93f526c832f091e3df4eed6eb2300a585751186..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
-import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
-
-import { NgSelectModule } from '@ng-select/ng-select';
-
-import { TimeComponent } from './time.component';
-import { FieldCriterion } from '../../../store/model';
-
-describe('[Search][Criteria][SearchType] Component: TimeComponent', () => {
-    @Component({
-        selector: `app-host`,
-        template: `<app-time [criterion]='criterion'></app-time>`
-    })
-    class TestHostComponent {
-        @ViewChild(TimeComponent, { static: false })
-        public testedComponent: TimeComponent;
-        public criterion: FieldCriterion = undefined;
-    }
-
-    @Component({ selector: 'app-operator', template: '' })
-    class OperatorStubComponent {
-        @Input() operator: string;
-        @Input() searchType: string;
-        @Input() advancedForm: boolean;
-        @Input() disabled: boolean;
-        @Output() changeOperator: EventEmitter<string> = new EventEmitter();
-    }
-
-    let testHostComponent: TestHostComponent;
-    let testHostFixture: ComponentFixture<TestHostComponent>;
-    let testedComponent: TimeComponent;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                TimeComponent,
-                TestHostComponent,
-                OperatorStubComponent
-            ],
-            imports: [NgSelectModule, FormsModule, ReactiveFormsModule]
-        });
-        testHostFixture = TestBed.createComponent(TestHostComponent);
-        testHostComponent = testHostFixture.componentInstance;
-        testHostFixture.detectChanges();
-        testedComponent = testHostComponent.testedComponent;
-    }));
-
-    it('should create the component', () => {
-        expect(testedComponent).toBeTruthy();
-    });
-
-    it('#getDefault() should enable and not fill form as criterion not defined in host component', () => {
-        expect(testedComponent.hh.value).toBeNull();
-        expect(testedComponent.hh.enabled).toBeTruthy();
-        expect(testedComponent.mm.value).toBeNull();
-        expect(testedComponent.mm.enabled).toBeTruthy();
-    });
-
-    it('#getDefault() should fill and disable form if criterion is defined', () => {
-        const criterion = { id: 1, type: 'field', operator: 'eq', value: '15:47' } as FieldCriterion;
-        const expectedHour = '15';
-        const expectedMinute = '47';
-        testedComponent.getDefault(criterion);
-        expect(testedComponent.hh.value).toEqual(expectedHour);
-        expect(testedComponent.hh.disabled).toBeTruthy();
-        expect(testedComponent.mm.value).toEqual(expectedMinute);
-        expect(testedComponent.mm.disabled).toBeTruthy();
-    });
-
-    it('#initTime(t) should return an array of string with 2 digits from 0 to t', () => {
-        const n = 10;
-        expect(testedComponent.initTime(n).length).toEqual(n);
-        expect(testedComponent.initTime(n)[5]).toEqual('05');
-    });
-
-    it('#changeOperator() should change the operator', () => {
-        expect(testedComponent.operator).toBeUndefined();
-        testedComponent.changeOperator('toto');
-        expect(testedComponent.operator).toBe('toto');
-    });
-
-    it('raises the add criterion event when clicked', () => {
-        testedComponent.id = 1;
-        const operator = 'eq';
-        testedComponent.operator = operator;
-        testedComponent.hh = new FormControl('15');
-        testedComponent.mm = new FormControl('47');
-        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value: '15:47' } as FieldCriterion;
-        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
-        testedComponent.emitAdd();
-    });
-
-    it('raises the delete criterion event when clicked', () => {
-        testedComponent.id = 1;
-        testedComponent.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
-        testedComponent.emitDelete();
-    });
-});
diff --git a/client/src/app/instance/search/components/result/download.component.ts b/client/src/app/instance/search/components/result/download.component.ts
index aedba8081c7c8c2412e7a397ddf8a12c0a0cc8c0..729dfe595cc1f1e8d5484bf2d91b84311d35ca44 100644
--- a/client/src/app/instance/search/components/result/download.component.ts
+++ b/client/src/app/instance/search/components/result/download.component.ts
@@ -13,6 +13,7 @@ import { Criterion, criterionToString } from '../../../store/models';
 import { Dataset } from 'src/app/metamodel/models';
 import { getHost as host } from 'src/app/shared/utils';
 // import { ConeSearch } from '../../../shared/cone-search/store/model';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-download',
@@ -36,6 +37,8 @@ export class DownloadComponent {
     @Input() sampRegistered: boolean;
     @Output() broadcast: EventEmitter<string> = new EventEmitter();
 
+    constructor(private appConfig: AppConfigService) { }
+
     /**
      * Returns dataset label.
      *
@@ -76,7 +79,7 @@ export class DownloadComponent {
      * @return string
      */
     getUrl(format: string): string {
-        let query: string = host() + '/search/' + this.datasetSelected + '?a=' + this.outputList.join(';');
+        let query: string = host(this.appConfig.apiUrl) + '/search/' + this.datasetSelected + '?a=' + this.outputList.join(';');
         if (this.criteriaList.length > 0) {
             query += '&c=' + this.criteriaList.map(criterion => criterionToString(criterion)).join(';');
         }
@@ -88,7 +91,7 @@ export class DownloadComponent {
     }
 
     getUrlArchive(): string {
-        let query: string = host() + '/archive/' + this.datasetSelected + '?a=' + this.outputList.join(';');
+        let query: string = host(this.appConfig.apiUrl) + '/archive/' + this.datasetSelected + '?a=' + this.outputList.join(';');
         if (this.criteriaList.length > 0) {
             query += '&c=' + this.criteriaList.map(criterion => criterionToString(criterion)).join(';');
         }
diff --git a/client/src/app/instance/search/components/result/url-display.component.ts b/client/src/app/instance/search/components/result/url-display.component.ts
index 469bc9b420a9710c4a959a99c088dbf01d572822..1ed461ba64d2d44b402b2cf8eaedf98d59e5024d 100644
--- a/client/src/app/instance/search/components/result/url-display.component.ts
+++ b/client/src/app/instance/search/components/result/url-display.component.ts
@@ -14,6 +14,7 @@ import { ToastrService } from 'ngx-toastr';
 import { Criterion, ConeSearch, criterionToString } from 'src/app/instance/store/models';
 import { Dataset } from 'src/app/metamodel/models';
 import { getHost as host } from 'src/app/shared/utils';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-url-display',
@@ -32,7 +33,7 @@ export class UrlDisplayComponent {
     @Input() criteriaList: Criterion[];
     @Input() outputList: number[];
 
-    constructor(private toastr: ToastrService) { }
+    constructor(private toastr: ToastrService, private appConfig: AppConfigService) { }
 
     /**
      * Checks if URL display is enabled.
@@ -55,7 +56,7 @@ export class UrlDisplayComponent {
      * @return string
      */
     getUrl(): string {
-        let query: string = host() + '/search/' + this.datasetSelected + '?a=' + this.outputList.join(';');
+        let query: string = host(this.appConfig.apiUrl) + '/search/' + this.datasetSelected + '?a=' + this.outputList.join(';');
         if (this.criteriaList.length > 0) {
             query += '&c=' + this.criteriaList.map(criterion => criterionToString(criterion)).join(';');
         }
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts b/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts
index 74fc0a7f9a9f67367faeae9ea88d2bd404a25a12..19bcc38572ede27f043ee814f2f330d76eabeb25 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/download-renderer.component.ts
@@ -11,6 +11,7 @@ import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 
 import { DownloadRendererConfig } from 'src/app/metamodel/models/renderers/download-renderer-config.model';
 import { getHost } from 'src/app/shared/utils';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-download-renderer',
@@ -26,13 +27,15 @@ export class DownloadRendererComponent {
     @Input() datasetName: string;
     @Input() config: DownloadRendererConfig;
 
+    constructor(private appConfig: AppConfigService) { }
+
     /**
      * Returns link href.
      *
      * @return string
      */
     getHref(): string {
-        return getHost() + '/download-file/' + this.datasetName + '/' + this.value;
+        return getHost(this.appConfig.apiUrl) + '/download-file/' + this.datasetName + '/' + this.value;
     }
 
     /**
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/image-renderer.component.ts b/client/src/app/instance/shared-search/components/datatable/renderer/image-renderer.component.ts
index 75521e7d2816346299a40bebc41cee587f5a0e40..677dfd73c7e0f8baf9cd8bd11e1a74015f49c42a 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/image-renderer.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/image-renderer.component.ts
@@ -13,7 +13,7 @@ import { BsModalService } from 'ngx-bootstrap/modal';
 import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
 
 import { ImageRendererConfig } from 'src/app/metamodel/models/renderers';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-image-renderer',
@@ -28,11 +28,10 @@ export class ImageRendererComponent {
     @Input() value: string | number;
     @Input() datasetName: string;
     @Input() config: ImageRendererConfig;
-    private SERVICES_PATH: string = environment.servicesUrl;
 
     modalRef: BsModalRef;
 
-    constructor(private modalService: BsModalService) { }
+    constructor(private modalService: BsModalService, private appConfig: AppConfigService) { }
 
     openModal(template: TemplateRef<any>) {
         this.modalRef = this.modalService.show(template);
@@ -45,7 +44,7 @@ export class ImageRendererComponent {
      */
     getValue(): string {
         if (this.config.type === 'fits') {
-            return `${this.SERVICES_PATH}/fits-to-png/${this.datasetName}?filename=${this.value}`
+            return `${this.appConfig.servicesUrl}/fits-to-png/${this.datasetName}?filename=${this.value}`
                 + `&stretch=linear&pmin=0.25&pmax=99.75&axes=true`;
         } else {
             return this.value as string;
diff --git a/client/src/app/instance/store/services/detail.service.ts b/client/src/app/instance/store/services/detail.service.ts
index e1b5db35066ac3cdcf46e331755d201835014ab4..3b946c5b17a2e9c649be86c4fa267ebe32f4907d 100644
--- a/client/src/app/instance/store/services/detail.service.ts
+++ b/client/src/app/instance/store/services/detail.service.ts
@@ -12,7 +12,7 @@ import { HttpClient } from '@angular/common/http';
 
 import { Observable } from 'rxjs';
 
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 /**
@@ -20,10 +20,7 @@ import { environment } from 'src/environments/environment';
  * @classdesc Detail service.
  */
 export class DetailService {
-    private API_PATH: string = environment.apiUrl + '/search/';
-    private SERVICES_PATH: string = environment.servicesUrl;
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     /**
      * Retrieves object details for the given parameters.
@@ -37,7 +34,7 @@ export class DetailService {
      */
     retrieveObject(dname: string, criterionId: number, objectSelected: string, outputList: number[]): Observable<any[]> {
         const query = dname + '?c=' + criterionId + '::eq::' + objectSelected + '&a=' + outputList.join(';');
-        return this.http.get<any[]>(this.API_PATH + query);
+        return this.http.get<any[]>(this.config.apiUrl + '/search/' + query);
     }
 
     /**
@@ -48,6 +45,6 @@ export class DetailService {
      * @return Observable<string>
      */
     retrieveSpectra(dname: string, spectraFile: string): Observable<string> {
-        return this.http.get(this.SERVICES_PATH + '/spectra-to-csv/' + dname + '?filename=' + spectraFile, { responseType: 'text' });
+        return this.http.get(this.config.servicesUrl + '/spectra-to-csv/' + dname + '?filename=' + spectraFile, { responseType: 'text' });
     }
 }
diff --git a/client/src/app/instance/store/services/samp.service.ts b/client/src/app/instance/store/services/samp.service.ts
index c4a8a9dbb612ac46a8690fe0d70826875aade414..1429c9bf7394825137519be7bf9830021822d274 100644
--- a/client/src/app/instance/store/services/samp.service.ts
+++ b/client/src/app/instance/store/services/samp.service.ts
@@ -11,7 +11,7 @@ import { Injectable } from '@angular/core';
 
 import { Observable } from 'rxjs';
 
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 declare var samp: any;
 
@@ -23,8 +23,8 @@ declare var samp: any;
 export class SampService {
     private connector = null;
     
-    constructor() {
-        const baseUrl = window.location.protocol + "//" + window.location.host + environment.baseHref;
+    constructor(private config: AppConfigService) {
+        const baseUrl = window.location.protocol + "//" + window.location.host + this.config.baseHref;
         const meta = {
             "samp.name": "ANIS",
             "samp.description.text": "AstroNomical Information System",
diff --git a/client/src/app/instance/store/services/search.service.ts b/client/src/app/instance/store/services/search.service.ts
index 7f5321317525144fe8d1e7da4670f892d5ac6d7c..171814321dc1e71d546be92e53c614ccd4d36410 100644
--- a/client/src/app/instance/store/services/search.service.ts
+++ b/client/src/app/instance/store/services/search.service.ts
@@ -12,7 +12,7 @@ import { HttpClient } from '@angular/common/http';
 
 import { Observable } from 'rxjs';
 
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 /**
@@ -20,9 +20,7 @@ import { environment } from 'src/environments/environment';
  * @classdesc Search service.
  */
 export class SearchService {
-    API_PATH: string = environment.apiUrl;
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     /**
      * Retrieves results for the given parameters.
@@ -32,7 +30,7 @@ export class SearchService {
      * @return Observable<any[]>
      */
     retrieveData(query: string): Observable<any[]> {
-        return this.http.get<any[]>(this.API_PATH + '/search/' + query);
+        return this.http.get<any[]>(this.config.apiUrl + '/search/' + query);
     }
 
     /**
@@ -43,6 +41,6 @@ export class SearchService {
      * @return Observable<{ nb: number }[]>
      */
     retrieveDataLength(query: string): Observable<{ nb: number }[]> {
-        return this.http.get<{ nb: number }[]>(this.API_PATH + '/search/' + query);
+        return this.http.get<{ nb: number }[]>(this.config.apiUrl + '/search/' + query);
     }
 }
diff --git a/client/src/app/metamodel/services/attribute-distinct.service.ts b/client/src/app/metamodel/services/attribute-distinct.service.ts
index bc89ba7c5f2127f4eda6d7fb5b24e91b66eb42f7..e5466159309a507362dfe260e8f33073516a4fd5 100644
--- a/client/src/app/metamodel/services/attribute-distinct.service.ts
+++ b/client/src/app/metamodel/services/attribute-distinct.service.ts
@@ -13,15 +13,13 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Attribute } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class AttributeDistinctService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveAttributeDistinctList(datasetName: string, attribute: Attribute): Observable<string[]> {
-        return this.http.get<string[]>(this.API_PATH + 'dataset/' + datasetName + '/attribute/' + attribute.id + '/distinct');
+        return this.http.get<string[]>(this.config.apiUrl + '/dataset/' + datasetName + '/attribute/' + attribute.id + '/distinct');
     }
 }
diff --git a/client/src/app/metamodel/services/attribute.service.ts b/client/src/app/metamodel/services/attribute.service.ts
index 08c84d6cfbc6ac70cfa3c755d00c82d4bce46860..9c2583c71f1c1af80bda0ad53daa8c9402ff11fc 100644
--- a/client/src/app/metamodel/services/attribute.service.ts
+++ b/client/src/app/metamodel/services/attribute.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Attribute } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class AttributeService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveAttributeList(datasetName: string): Observable<Attribute[]> {
-        return this.http.get<Attribute[]>(this.API_PATH + 'dataset/' + datasetName + '/attribute');
+        return this.http.get<Attribute[]>(this.config.apiUrl + '/dataset/' + datasetName + '/attribute');
     }
 
     addAttribute(datasetName: string, attribute: Attribute): Observable<Attribute> {
-        return this.http.post<Attribute>(this.API_PATH + 'dataset/' + datasetName + '/attribute', attribute);
+        return this.http.post<Attribute>(this.config.apiUrl + '/dataset/' + datasetName + '/attribute', attribute);
     }
 
     editAttribute(datasetName: string, attribute: Attribute): Observable<Attribute> {
-        return this.http.put<Attribute>(this.API_PATH + 'dataset/' + datasetName + '/attribute/' + attribute.id, attribute);
+        return this.http.put<Attribute>(this.config.apiUrl + '/dataset/' + datasetName + '/attribute/' + attribute.id, attribute);
     }
 
     deleteAttribute(datasetName: string, attribute: Attribute) {
-        return this.http.delete(this.API_PATH + 'dataset/' + datasetName + '/attribute/' + attribute.id); 
+        return this.http.delete(this.config.apiUrl + '/dataset/' + datasetName + '/attribute/' + attribute.id); 
     }
 }
diff --git a/client/src/app/metamodel/services/column.service.ts b/client/src/app/metamodel/services/column.service.ts
index 881006be1a815b3960e5be1d012e1dce0a25d97d..0da2af5345283d3374916e1fc40b4bc4cd9ef2cd 100644
--- a/client/src/app/metamodel/services/column.service.ts
+++ b/client/src/app/metamodel/services/column.service.ts
@@ -13,15 +13,13 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Column } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class ColumnService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveColumns(datasetName: string): Observable<Column[]> {
-        return this.http.get<Column[]>(this.API_PATH + 'dataset/' + datasetName + '/column');
+        return this.http.get<Column[]>(this.config.apiUrl + '/dataset/' + datasetName + '/column');
     }
 }
diff --git a/client/src/app/metamodel/services/criteria-family.service.ts b/client/src/app/metamodel/services/criteria-family.service.ts
index 32e2ecfabedacdf8282e5e97626e35d25ec6d5a3..09d6dd21a477f150f8de5f5fe0a6796e0860966f 100644
--- a/client/src/app/metamodel/services/criteria-family.service.ts
+++ b/client/src/app/metamodel/services/criteria-family.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { CriteriaFamily } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class CriteriaFamilyService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveCriteriaFamilyList(datasetName: string): Observable<CriteriaFamily[]> {
-        return this.http.get<CriteriaFamily[]>(this.API_PATH + 'dataset/' + datasetName + '/criteria-family');
+        return this.http.get<CriteriaFamily[]>(this.config.apiUrl + '/dataset/' + datasetName + '/criteria-family');
     }
 
     addCriteriaFamily(datasetName: string, newCriteriaFamily: CriteriaFamily): Observable<CriteriaFamily> {
-        return this.http.post<CriteriaFamily>(this.API_PATH + 'dataset/' + datasetName + '/criteria-family', newCriteriaFamily);
+        return this.http.post<CriteriaFamily>(this.config.apiUrl + '/dataset/' + datasetName + '/criteria-family', newCriteriaFamily);
     }
 
     editCriteriaFamily(criteriaFamily: CriteriaFamily): Observable<CriteriaFamily> {
-        return this.http.put<CriteriaFamily>(this.API_PATH + 'criteria-family/' + criteriaFamily.id, criteriaFamily);
+        return this.http.put<CriteriaFamily>(this.config.apiUrl + '/criteria-family/' + criteriaFamily.id, criteriaFamily);
     }
 
     deleteCriteriaFamily(criteriaFamilyId: number) {
-        return this.http.delete(this.API_PATH + 'criteria-family/' + criteriaFamilyId);
+        return this.http.delete(this.config.apiUrl + '/criteria-family/' + criteriaFamilyId);
     }
 }
diff --git a/client/src/app/metamodel/services/database.service.ts b/client/src/app/metamodel/services/database.service.ts
index 567254fd3a852f25994ffc31b4264168bd092164..c569d351670179e73ccf3067dafa12bfdd6628e3 100644
--- a/client/src/app/metamodel/services/database.service.ts
+++ b/client/src/app/metamodel/services/database.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Database } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class DatabaseService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveDatabaseList(): Observable<Database[]> {
-        return this.http.get<Database[]>(this.API_PATH + 'database');
+        return this.http.get<Database[]>(this.config.apiUrl + '/database');
     }
 
     addDatabase(newDatabase: Database): Observable<Database> {
-        return this.http.post<Database>(this.API_PATH + 'database', newDatabase);
+        return this.http.post<Database>(this.config.apiUrl + '/database', newDatabase);
     }
 
     editDatabase(database: Database): Observable<Database> {
-        return this.http.put<Database>(this.API_PATH + 'database/' + database.id, database);
+        return this.http.put<Database>(this.config.apiUrl + '/database/' + database.id, database);
     }
 
     deleteDatabase(databaseId: number) {
-        return this.http.delete(this.API_PATH + 'database/' + databaseId);
+        return this.http.delete(this.config.apiUrl + '/database/' + databaseId);
     }
 }
diff --git a/client/src/app/metamodel/services/dataset-family.service.ts b/client/src/app/metamodel/services/dataset-family.service.ts
index 490a14af0b92afe8c121d4d54938b489826e51c6..96fc34460b1c5520a754f5f1afb9edafcde8d821 100644
--- a/client/src/app/metamodel/services/dataset-family.service.ts
+++ b/client/src/app/metamodel/services/dataset-family.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { DatasetFamily } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class DatasetFamilyService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveDatasetFamilyList(instanceName: string): Observable<DatasetFamily[]> {
-        return this.http.get<DatasetFamily[]>(this.API_PATH + 'instance/' + instanceName + '/dataset-family');
+        return this.http.get<DatasetFamily[]>(this.config.apiUrl + '/instance/' + instanceName + '/dataset-family');
     }
 
     addDatasetFamily(instanceName: string, newDatasetFamily: DatasetFamily): Observable<DatasetFamily> {
-        return this.http.post<DatasetFamily>(this.API_PATH + 'instance/' + instanceName + '/dataset-family', newDatasetFamily);
+        return this.http.post<DatasetFamily>(this.config.apiUrl + '/instance/' + instanceName + '/dataset-family', newDatasetFamily);
     }
 
     editDatasetFamily(datasetFamily: DatasetFamily): Observable<DatasetFamily> {
-        return this.http.put<DatasetFamily>(this.API_PATH + 'dataset-family/' + datasetFamily.id, datasetFamily);
+        return this.http.put<DatasetFamily>(this.config.apiUrl + '/dataset-family/' + datasetFamily.id, datasetFamily);
     }
 
     deleteDatasetFamily(datasetFamilyId: number) {
-        return this.http.delete(this.API_PATH + 'dataset-family/' + datasetFamilyId);
+        return this.http.delete(this.config.apiUrl + '/dataset-family/' + datasetFamilyId);
     }
 }
diff --git a/client/src/app/metamodel/services/dataset.service.ts b/client/src/app/metamodel/services/dataset.service.ts
index 4b7c9b8369c11e0f9d2682ea36bd2f16f6a11d05..e8109448278f7e10f9e0f486eb25513c988c3265 100644
--- a/client/src/app/metamodel/services/dataset.service.ts
+++ b/client/src/app/metamodel/services/dataset.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Dataset } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class DatasetService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveDatasetList(instanceName: string): Observable<Dataset[]> {
-        return this.http.get<Dataset[]>(this.API_PATH + 'instance/' + instanceName + '/dataset');
+        return this.http.get<Dataset[]>(this.config.apiUrl + '/instance/' + instanceName + '/dataset');
     }
 
     addDataset(newDataset: Dataset): Observable<Dataset> {
-        return this.http.post<Dataset>(this.API_PATH + 'dataset-family/' + newDataset.id_dataset_family + '/dataset', newDataset);
+        return this.http.post<Dataset>(this.config.apiUrl + '/dataset-family/' + newDataset.id_dataset_family + '/dataset', newDataset);
     }
 
     editDataset(dataset: Dataset): Observable<Dataset> {
-        return this.http.put<Dataset>(this.API_PATH + 'dataset/' + dataset.name, dataset);
+        return this.http.put<Dataset>(this.config.apiUrl + '/dataset/' + dataset.name, dataset);
     }
 
     deleteDataset(datasetName: string) {
-        return this.http.delete(this.API_PATH + 'dataset/' + datasetName);
+        return this.http.delete(this.config.apiUrl + '/dataset/' + datasetName);
     }
 }
diff --git a/client/src/app/metamodel/services/group.service.ts b/client/src/app/metamodel/services/group.service.ts
index c4cbdde647f2b446576a48834e6de42d2913995b..b83489bebbbc04d63fcbfb237ef359266bd27348 100644
--- a/client/src/app/metamodel/services/group.service.ts
+++ b/client/src/app/metamodel/services/group.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Group } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class GroupService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveGroupList(instanceName: string): Observable<Group[]> {
-        return this.http.get<Group[]>(this.API_PATH + 'instance/' + instanceName +  '/group');
+        return this.http.get<Group[]>(this.config.apiUrl + '/instance/' + instanceName +  '/group');
     }
 
     addGroup(instanceName: string, newGroup: Group): Observable<Group> {
-        return this.http.post<Group>(this.API_PATH + 'instance/' + instanceName + '/group', newGroup);
+        return this.http.post<Group>(this.config.apiUrl + '/instance/' + instanceName + '/group', newGroup);
     }
 
     editGroup(group: Group): Observable<Group> {
-        return this.http.put<Group>(this.API_PATH + 'group/' + group.id, group);
+        return this.http.put<Group>(this.config.apiUrl + '/group/' + group.id, group);
     }
 
     deleteGroup(groupId: number) {
-        return this.http.delete(this.API_PATH + 'group/' + groupId);
+        return this.http.delete(this.config.apiUrl + '/group/' + groupId);
     }
 }
diff --git a/client/src/app/metamodel/services/instance.service.ts b/client/src/app/metamodel/services/instance.service.ts
index a5f4ab0e57f7dbdd4db7cebf56ff7ae8179e472a..d90f7f157acc9051687bd93469a49c46f9aa6e7f 100644
--- a/client/src/app/metamodel/services/instance.service.ts
+++ b/client/src/app/metamodel/services/instance.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Instance } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class InstanceService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveInstanceList(): Observable<Instance[]> {
-        return this.http.get<Instance[]>(this.API_PATH + 'instance');
+        return this.http.get<Instance[]>(this.config.apiUrl + '/instance');
     }
 
     addInstance(newInstance: Instance): Observable<Instance> {
-        return this.http.post<Instance>(this.API_PATH + 'instance', newInstance);
+        return this.http.post<Instance>(this.config.apiUrl + '/instance', newInstance);
     }
 
     editInstance(instance: Instance): Observable<Instance> {
-        return this.http.put<Instance>(this.API_PATH + 'instance/' + instance.name, instance);
+        return this.http.put<Instance>(this.config.apiUrl + '/instance/' + instance.name, instance);
     }
 
     deleteInstance(instanceName: string) {
-        return this.http.delete(this.API_PATH + 'instance/' + instanceName);
+        return this.http.delete(this.config.apiUrl + '/instance/' + instanceName);
     }
 }
diff --git a/client/src/app/metamodel/services/output-category.service.ts b/client/src/app/metamodel/services/output-category.service.ts
index 2d35525d6071b2c049dba7025a8b42f943e0005f..5cc3e49dbd165c348df8dc60b7389b4592ff4e45 100644
--- a/client/src/app/metamodel/services/output-category.service.ts
+++ b/client/src/app/metamodel/services/output-category.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { OutputCategory } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class OutputCategoryService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveOutputCategoryList(datasetName: string): Observable<OutputCategory[]> {
-        return this.http.get<OutputCategory[]>(this.API_PATH + 'dataset/' + datasetName + '/output-category');
+        return this.http.get<OutputCategory[]>(this.config.apiUrl + '/dataset/' + datasetName + '/output-category');
     }
 
     addOutputCategory(newOutputCategory: OutputCategory): Observable<OutputCategory> {
-        return this.http.post<OutputCategory>(this.API_PATH + 'output-family/' + newOutputCategory.id_output_family + '/output-category', newOutputCategory);
+        return this.http.post<OutputCategory>(this.config.apiUrl + '/output-family/' + newOutputCategory.id_output_family + '/output-category', newOutputCategory);
     }
 
     editOutputCategory(outputCategory: OutputCategory): Observable<OutputCategory> {
-        return this.http.put<OutputCategory>(this.API_PATH + 'output-category/' + outputCategory.id, outputCategory);
+        return this.http.put<OutputCategory>(this.config.apiUrl + '/output-category/' + outputCategory.id, outputCategory);
     }
 
     deleteOutputCategory(outputCategoryId: number) {
-        return this.http.delete(this.API_PATH + 'output-category/' + outputCategoryId);
+        return this.http.delete(this.config.apiUrl + '/output-category/' + outputCategoryId);
     }
 }
diff --git a/client/src/app/metamodel/services/output-family.service.ts b/client/src/app/metamodel/services/output-family.service.ts
index 61494ed2cbcf8b2da2959c40290073c97cd55763..825e191902bac95960727424fdbe483c9f9d1a5a 100644
--- a/client/src/app/metamodel/services/output-family.service.ts
+++ b/client/src/app/metamodel/services/output-family.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { OutputFamily } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class OutputFamilyService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveOutputFamilyList(datasetName: string): Observable<OutputFamily[]> {
-        return this.http.get<OutputFamily[]>(this.API_PATH + 'dataset/' + datasetName + '/output-family');
+        return this.http.get<OutputFamily[]>(this.config.apiUrl + '/dataset/' + datasetName + '/output-family');
     }
 
     addOutputFamily(datasetName: string, newOutputFamily: OutputFamily): Observable<OutputFamily> {
-        return this.http.post<OutputFamily>(this.API_PATH + 'dataset/' + datasetName + '/output-family', newOutputFamily);
+        return this.http.post<OutputFamily>(this.config.apiUrl + '/dataset/' + datasetName + '/output-family', newOutputFamily);
     }
 
     editOutputFamily(criteriaFamily: OutputFamily): Observable<OutputFamily> {
-        return this.http.put<OutputFamily>(this.API_PATH + 'output-family/' + criteriaFamily.id, criteriaFamily);
+        return this.http.put<OutputFamily>(this.config.apiUrl + '/output-family/' + criteriaFamily.id, criteriaFamily);
     }
 
     deleteOutputFamily(outputFamilyId: number) {
-        return this.http.delete(this.API_PATH + 'output-family/' + outputFamilyId);
+        return this.http.delete(this.config.apiUrl + '/output-family/' + outputFamilyId);
     }
 }
diff --git a/client/src/app/metamodel/services/root-directory.service.ts b/client/src/app/metamodel/services/root-directory.service.ts
index c34f2c41a9f92d24917e928aca14e97b4f135610..d173f00d10b99e3872109fac4823c225a2a5a674 100644
--- a/client/src/app/metamodel/services/root-directory.service.ts
+++ b/client/src/app/metamodel/services/root-directory.service.ts
@@ -13,15 +13,13 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { FileInfo } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class RootDirectoryService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveRootDirectory(path: string): Observable<FileInfo[]> {
-        return this.http.get<FileInfo[]>(this.API_PATH + 'file-explorer/' + path);
+        return this.http.get<FileInfo[]>(this.config.apiUrl + '/file-explorer/' + path);
     }
 }
diff --git a/client/src/app/metamodel/services/select-option.service.ts b/client/src/app/metamodel/services/select-option.service.ts
index 07012cfab5b1126b321c6bc5970229b24af65e7c..e18a0dd35e8b55fce4006525e6705909ec208881 100644
--- a/client/src/app/metamodel/services/select-option.service.ts
+++ b/client/src/app/metamodel/services/select-option.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { SelectOption } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class SelectOptionService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveSelectOptionList(): Observable<SelectOption[]> {
-        return this.http.get<SelectOption[]>(this.API_PATH + 'option');
+        return this.http.get<SelectOption[]>(this.config.apiUrl + '/option');
     }
 
     addSelectOption(settingsSelectOption: SelectOption): Observable<SelectOption> {
-        return this.http.post<SelectOption>(this.API_PATH + 'option', settingsSelectOption);
+        return this.http.post<SelectOption>(this.config.apiUrl + '/option', settingsSelectOption);
     }
 
     editSelectOption(settingsSelectOption: SelectOption): Observable<SelectOption> {
-        return this.http.put<SelectOption>(this.API_PATH + 'option/' + settingsSelectOption.id, settingsSelectOption);
+        return this.http.put<SelectOption>(this.config.apiUrl + '/option/' + settingsSelectOption.id, settingsSelectOption);
     }
 
     deleteSelectOption(id: number) {
-        return this.http.delete(this.API_PATH + 'option/' + id);
+        return this.http.delete(this.config.apiUrl + '/option/' + id);
     }
 }
diff --git a/client/src/app/metamodel/services/select.service.ts b/client/src/app/metamodel/services/select.service.ts
index e88b7289c73060638d29462bc0fa79c5ea3d18fe..b146a874930054099765f9939310be7be1ab7c6b 100644
--- a/client/src/app/metamodel/services/select.service.ts
+++ b/client/src/app/metamodel/services/select.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Select } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class SelectService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveSelectList(): Observable<Select[]> {
-        return this.http.get<Select[]>(this.API_PATH + 'select');
+        return this.http.get<Select[]>(this.config.apiUrl + '/select');
     }
 
     addSelect(select: Select): Observable<Select> {
-        return this.http.post<Select>(this.API_PATH + 'select', select);
+        return this.http.post<Select>(this.config.apiUrl + '/select', select);
     }
 
     editSelect(select: Select): Observable<Select> {
-        return this.http.put<Select>(this.API_PATH + 'select/' + select.name, select);
+        return this.http.put<Select>(this.config.apiUrl + '/select/' + select.name, select);
     }
 
     deleteSelect(name: string) {
-        return this.http.delete(this.API_PATH + 'select/' + name);
+        return this.http.delete(this.config.apiUrl + '/select/' + name);
     }
 }
diff --git a/client/src/app/metamodel/services/survey.service.ts b/client/src/app/metamodel/services/survey.service.ts
index 6f482874f18e4d0eb4a8bdd64d89f9d93359604a..faf0c1ea4d6eff2c1d72c60f2fcf551b81f843bd 100644
--- a/client/src/app/metamodel/services/survey.service.ts
+++ b/client/src/app/metamodel/services/survey.service.ts
@@ -13,27 +13,25 @@ import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
 
 import { Survey } from '../models';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class SurveyService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveSurveyList(): Observable<Survey[]> {
-        return this.http.get<Survey[]>(this.API_PATH + 'survey');
+        return this.http.get<Survey[]>(this.config.apiUrl + '/survey');
     }
 
     addSurvey(newSurvey: Survey): Observable<Survey> {
-        return this.http.post<Survey>(this.API_PATH + 'survey', newSurvey);
+        return this.http.post<Survey>(this.config.apiUrl + '/survey', newSurvey);
     }
 
     editSurvey(survey: Survey): Observable<Survey> {
-        return this.http.put<Survey>(this.API_PATH + 'survey/' + survey.name, survey);
+        return this.http.put<Survey>(this.config.apiUrl + '/survey/' + survey.name, survey);
     }
 
     deleteSurvey(surveyName: string) {
-        return this.http.delete(this.API_PATH + 'survey/' + surveyName);
+        return this.http.delete(this.config.apiUrl + '/survey/' + surveyName);
     }
 }
diff --git a/client/src/app/metamodel/services/table.service.ts b/client/src/app/metamodel/services/table.service.ts
index 96625dfd1e4221867c42c946a1ee262397c7aacf..3a3722b7f515825255cc08de75447101e4f65e5a 100644
--- a/client/src/app/metamodel/services/table.service.ts
+++ b/client/src/app/metamodel/services/table.service.ts
@@ -12,15 +12,13 @@ import { HttpClient } from '@angular/common/http';
 
 import { Observable } from 'rxjs';
 
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Injectable()
 export class TableService {
-    private API_PATH: string = environment.apiUrl + '/';
-
-    constructor(private http: HttpClient) { }
+    constructor(private http: HttpClient, private config: AppConfigService) { }
 
     retrieveTableList(idDatabase: number): Observable<string[]> {
-        return this.http.get<string[]>(this.API_PATH + 'database/' + idDatabase + '/table');
+        return this.http.get<string[]>(this.config.apiUrl + '/database/' + idDatabase + '/table');
     }
 }
diff --git a/client/src/app/portal/containers/portal-home.component.html b/client/src/app/portal/containers/portal-home.component.html
index 16f93da003ef45253778b21864f45a82588fee1a..7fab8209cb2ce3ec74a2982dbfec41e47c310268 100644
--- a/client/src/app/portal/containers/portal-home.component.html
+++ b/client/src/app/portal/containers/portal-home.component.html
@@ -3,6 +3,8 @@
         [links]="links"
         [isAuthenticated]="isAuthenticated | async"
         [userProfile]="userProfile | async"
+        [baseHref]="getBaseHref()"
+        [authenticationEnabled]="getAuthenticationEnabled()"
         (login)="login()"
         (logout)="logout()"
         (openEditProfile)="openEditProfile()">
diff --git a/client/src/app/portal/containers/portal-home.component.ts b/client/src/app/portal/containers/portal-home.component.ts
index 49c13a4734815c54578f74766dfbe4eb44c5226a..4405a97255d7be0a78375e525c845e425bf657c0 100644
--- a/client/src/app/portal/containers/portal-home.component.ts
+++ b/client/src/app/portal/containers/portal-home.component.ts
@@ -17,7 +17,7 @@ import * as authActions from 'src/app/auth/auth.actions';
 import * as authSelector from 'src/app/auth/auth.selector';
 import * as instanceActions from 'src/app/metamodel/actions/instance.actions';
 import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
-import { environment } from 'src/environments/environment';
+import { AppConfigService } from 'src/app/app-config.service';
 
 @Component({
     selector: 'app-portal-home',
@@ -42,7 +42,7 @@ export class PortalHomeComponent implements OnInit, OnDestroy {
 
     public userRolesSubscription: Subscription;
 
-    constructor(private store: Store<{ }>) {
+    constructor(private store: Store<{ }>, private config: AppConfigService) {
         this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
         this.userProfile = store.select(authSelector.selectUserProfile);
         this.userRoles = store.select(authSelector.selectUserRoles);
@@ -54,17 +54,25 @@ export class PortalHomeComponent implements OnInit, OnDestroy {
     ngOnInit() {
         this.store.dispatch(instanceActions.loadInstanceList());
         const adminLink = { label: 'Admin', icon: 'fas fa-tools', routerLink: '/admin' };
-        if (!environment.authenticationEnabled) {
+        if (!this.config.authenticationEnabled) {
             this.links.push(adminLink);
         } else {
             this.userRolesSubscription = this.userRoles.subscribe(userRoles => {
-                if (userRoles.includes(environment.adminRole)) {
+                if (userRoles.includes(this.config.adminRole)) {
                     this.links.push(adminLink);
                 }
             });
         }
     }
 
+    getBaseHref() {
+        return this.config.baseHref;
+    }
+
+    getAuthenticationEnabled() {
+        return this.config.authenticationEnabled;
+    }
+    
     login(): void {
         this.store.dispatch(authActions.login());
     }
diff --git a/client/src/app/shared/components/navbar.component.ts b/client/src/app/shared/components/navbar.component.ts
index c88f742045052debb35000bb6da163ff9f150e2f..733e55789e7d5b65bea006491b717cfdd758574f 100644
--- a/client/src/app/shared/components/navbar.component.ts
+++ b/client/src/app/shared/components/navbar.component.ts
@@ -10,7 +10,6 @@
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
 
 import { UserProfile } from 'src/app/auth/user-profile.model';
-import { environment } from 'src/environments/environment'
 
 @Component({
     selector: 'app-navbar',
@@ -22,10 +21,9 @@ export class NavbarComponent {
     @Input() links: {label: string, icon: string, routerLink: string}[];
     @Input() isAuthenticated: boolean;
     @Input() userProfile: UserProfile = null;
+    @Input() baseHref: string;
+    @Input() authenticationEnabled: boolean;
     @Output() login: EventEmitter<any> = new EventEmitter();
     @Output() logout: EventEmitter<any> = new EventEmitter();
     @Output() openEditProfile: EventEmitter<any> = new EventEmitter();
-    
-    baseHref: string = environment.baseHref;
-    authenticationEnabled: boolean = environment.authenticationEnabled;
 }
diff --git a/client/src/app/shared/utils.ts b/client/src/app/shared/utils.ts
index d5250e7ad656835ccfe3b3e758efbe9b0614cca0..b43ae07dacaf2fa56684636c8b8478909c2ff2e2 100644
--- a/client/src/app/shared/utils.ts
+++ b/client/src/app/shared/utils.ts
@@ -1,5 +1,3 @@
-import { environment } from 'src/environments/environment';
-
 /**
  * Returns strict url address.
  *
@@ -8,12 +6,12 @@ import { environment } from 'src/environments/environment';
  * @example
  * const url: string = getHost() + '/following-url/';
  */
-export const getHost = (): string => {
-    if (!environment.apiUrl.startsWith('http')) {
+export const getHost = (apiUrl: string): string => {
+    if (!apiUrl.startsWith('http')) {
         const url = window.location;
-        return url.protocol + '//' + url.host + environment.apiUrl;
+        return url.protocol + '//' + url.host + apiUrl;
     }
-    return environment.apiUrl;
+    return apiUrl;
 }
 
 /**
diff --git a/client/src/assets/app.config.json b/client/src/assets/app.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..0270c50c94d4db9f30d2c118f369f75b0c01884f
--- /dev/null
+++ b/client/src/assets/app.config.json
@@ -0,0 +1,10 @@
+{
+    "apiUrl": "http://localhost:8080",
+    "servicesUrl": "http://localhost:5000",
+    "baseHref": "/",
+    "authenticationEnabled": true,
+    "ssoAuthUrl": "http://localhost:8180/auth",
+    "ssoRealm": "anis",
+    "ssoClientId": "anis-client",
+    "adminRole": "anis_admin"
+}
\ No newline at end of file
diff --git a/client/src/environments/environment.prod.ts b/client/src/environments/environment.prod.ts
index e1cbd3d486ba86dd86d83f1fc989cc180d440b3d..5d0833162027e2147e99e72a1a94bb7d3cb62843 100644
--- a/client/src/environments/environment.prod.ts
+++ b/client/src/environments/environment.prod.ts
@@ -1,11 +1,3 @@
 export const environment = {
-    production: true,
-    apiUrl: '/server',
-    servicesUrl: '/services',
-    baseHref: '/',
-    authenticationEnabled: true,
-    ssoAuthUrl: 'https://keycloak.lam.fr/auth/',
-    ssoRealm: 'anis',
-    ssoClientId: 'anis-dev',
-    adminRole: 'anis_admin'
+    production: true
 };
diff --git a/client/src/environments/environment.ts b/client/src/environments/environment.ts
index 2a90c74fbae956590cdf6dc57fbe0e5beb69f079..458476a4df52c64d1a2abee43c6cb91d87abad77 100644
--- a/client/src/environments/environment.ts
+++ b/client/src/environments/environment.ts
@@ -3,15 +3,7 @@
 // The list of file replacements can be found in `angular.json`.
 
 export const environment = {
-    production: false,
-    apiUrl: 'http://localhost:8080',
-    servicesUrl: 'http://localhost:5000',
-    baseHref: '/',
-    authenticationEnabled: true,
-    ssoAuthUrl: 'http://localhost:8180/auth',
-    ssoRealm: 'anis',
-    ssoClientId: 'anis-client',
-    adminRole: 'anis_admin'
+    production: false
 };
 
 /*