diff --git a/config.json b/config.json new file mode 100644 index 0000000..bf83a94 --- /dev/null +++ b/config.json @@ -0,0 +1,5 @@ +{ + "production": true, + "apiBaseUrl": "https://jenkins-master-deephealth-unix01.ing.unimore.it/backend", + "clientId": "BnEOyrMrJ9qeMHeZO7lzF7kgpIFBLm2ZtBd8achT" +} diff --git a/docker-compose.yml b/docker-compose.yml index f1b0d7a..928c817 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,3 +13,5 @@ services: restart: unless-stopped environment: - JAVA_OPTS="-Xmx4548m -Xss512k" + volumes: + - ./config.json:/usr/src/app/src/assets/config.json diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e2bed7b..44bb6c9 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,52 +1,64 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { DeepHealthComponent } from './components/deep-health/deep-health.component'; -import { AppTabsComponent } from './components/app-tabs/app-tabs.component'; -import { PowerUserComponent } from './components/power-user/power-user.component'; -import { HttpClientModule, HTTP_INTERCEPTORS} from '@angular/common/http'; -import { AppMaterialModule } from './app-material/app-material.module'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ProjectComponent } from './components/project/project.component'; -import { ConfirmDialogComponent } from './components/confirm-dialog/confirm-dialog.component'; -import { FormsModule } from '@angular/forms'; -import { ReactiveFormsModule } from '@angular/forms'; -import { HttpClient } from '@angular/common/http'; -import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { TranslateService } from '@ngx-translate/core'; -import { ConfirmDialogTrainComponent } from './components/confirm-dialog-train/confirm-dialog-train.component'; -import { CreateProjectDialogComponent } from './components/create-project-dialog/create-project-dialog.component'; -import { UploadDatasetsDialogComponent } from './components/upload-datasets-dialog/upload-datasets-dialog.component'; -import { UpdateWeightDialogComponent } from './components/update-weight-dialog/update-weight-dialog.component'; -import { ShowOutputDetailsDialogComponent } from './components/show-output-details-dialog/show-output-details-dialog.component'; -import { InterceptorService } from './services/interceptor.service'; -import { AuthService } from './services/auth.service'; -import { OAuthModule } from 'angular-oauth2-oidc'; -import { RegisterUserComponent } from './components/register-user/register-user.component'; -import { LoginUserComponent } from './components/login-user/login-user.component'; -import { ShowProfileDetailsDialogComponent } from './components/show-profile-details-dialog/show-profile-details-dialog.component'; -import { AuthGuard } from './auth.guard'; -import { ResetPasswordComponent } from './components/reset-password/reset-password.component'; -import { FilterPipe } from './components/pipes/filter.pipe'; -import { DeleteDialogComponent } from './components/delete-dialog/delete-dialog.component'; -import { ProgressSpinnerDialogComponent } from './components/progress-spinner-dialog/progress-spinner-dialog.component'; -import { ProcessFilterPipe } from './components/pipes/process-filter.pipe'; -import { DropdownComponent } from './components/dynamic-components/dropdown/dropdown.component'; -import { InputIntegerComponent } from './components/dynamic-components/input-integer/input-integer.component'; -import { InputFloatComponent } from './components/dynamic-components/input-float/input-float.component'; -import { InputTextComponent } from './components/dynamic-components/input-text/input-text.component'; -import { BooleanComponent } from './components/dynamic-components/boolean/boolean.component'; +import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; +import { RouterModule, Routes } from "@angular/router"; +import { TranslateLoader, TranslateModule } from "@ngx-translate/core"; + +import { AppConfigService } from "./services/config.service"; +import { AppMaterialModule } from "./app-material/app-material.module"; +import { AppTabsComponent } from "./components/app-tabs/app-tabs.component"; +import { AuthGuard } from "./auth.guard"; +import { AuthService } from "./services/auth.service"; +import { BooleanComponent } from "./components/dynamic-components/boolean/boolean.component"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { ConfirmDialogComponent } from "./components/confirm-dialog/confirm-dialog.component"; +import { ConfirmDialogTrainComponent } from "./components/confirm-dialog-train/confirm-dialog-train.component"; +import { CreateProjectDialogComponent } from "./components/create-project-dialog/create-project-dialog.component"; +import { DeepHealthComponent } from "./components/deep-health/deep-health.component"; +import { DeleteDialogComponent } from "./components/delete-dialog/delete-dialog.component"; +import { DropdownComponent } from "./components/dynamic-components/dropdown/dropdown.component"; +import { FilterPipe } from "./components/pipes/filter.pipe"; +import { FormsModule } from "@angular/forms"; +import { HttpClient } from "@angular/common/http"; +import { InputFloatComponent } from "./components/dynamic-components/input-float/input-float.component"; +import { InputIntegerComponent } from "./components/dynamic-components/input-integer/input-integer.component"; +import { InputTextComponent } from "./components/dynamic-components/input-text/input-text.component"; +import { InterceptorService } from "./services/interceptor.service"; +import { LoginUserComponent } from "./components/login-user/login-user.component"; +import { NgModule } from "@angular/core"; +import { OAuthModule } from "angular-oauth2-oidc"; +import { PowerUserComponent } from "./components/power-user/power-user.component"; +import { ProcessFilterPipe } from "./components/pipes/process-filter.pipe"; +import { ProgressSpinnerDialogComponent } from "./components/progress-spinner-dialog/progress-spinner-dialog.component"; +import { ProjectComponent } from "./components/project/project.component"; +import { ReactiveFormsModule } from "@angular/forms"; +import { RegisterUserComponent } from "./components/register-user/register-user.component"; +import { ResetPasswordComponent } from "./components/reset-password/reset-password.component"; +import { ShowOutputDetailsDialogComponent } from "./components/show-output-details-dialog/show-output-details-dialog.component"; +import { ShowProfileDetailsDialogComponent } from "./components/show-profile-details-dialog/show-profile-details-dialog.component"; +import { TranslateHttpLoader } from "@ngx-translate/http-loader"; +import { TranslateService } from "@ngx-translate/core"; +import { UpdateWeightDialogComponent } from "./components/update-weight-dialog/update-weight-dialog.component"; +import { UploadDatasetsDialogComponent } from "./components/upload-datasets-dialog/upload-datasets-dialog.component"; export function createTranslateLoader(http: HttpClient) { - return new TranslateHttpLoader(http, './assets/i18n/', '.json'); + return new TranslateHttpLoader(http, "./assets/i18n/", ".json"); +} + +export function initConfig(appConfig: AppConfigService) { + return (): Promise => { + return appConfig.loadConfig(); + }; } const routes: Routes = [ - { path: '', component: LoginUserComponent }, - { path: 'power-user', component: PowerUserComponent, canActivate: [AuthGuard] }, - { path: 'project', component: ProjectComponent, canActivate: [AuthGuard] }, - { path: 'register', component: RegisterUserComponent }, - { path: 'reset-password', component: ResetPasswordComponent } + { path: "", component: LoginUserComponent }, + { + path: "power-user", + component: PowerUserComponent, + canActivate: [AuthGuard], + }, + { path: "project", component: ProjectComponent, canActivate: [AuthGuard] }, + { path: "register", component: RegisterUserComponent }, + { path: "reset-password", component: ResetPasswordComponent }, ]; @NgModule({ @@ -73,7 +85,7 @@ const routes: Routes = [ InputTextComponent, InputIntegerComponent, InputFloatComponent, - BooleanComponent + BooleanComponent, ], imports: [ RouterModule.forRoot(routes), @@ -86,18 +98,19 @@ const routes: Routes = [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, - useFactory: (createTranslateLoader), - deps: [HttpClient] - } + useFactory: createTranslateLoader, + deps: [HttpClient], + }, }), - OAuthModule.forRoot() + OAuthModule.forRoot(), ], - exports: [RouterModule, + exports: [ + RouterModule, AppMaterialModule, HttpClientModule, FormsModule, ReactiveFormsModule, - TranslateModule + TranslateModule, ], entryComponents: [ ConfirmDialogComponent, @@ -109,23 +122,26 @@ const routes: Routes = [ ShowProfileDetailsDialogComponent, DeleteDialogComponent, ProgressSpinnerDialogComponent, - DropdownComponent, - InputIntegerComponent, - InputFloatComponent, + DropdownComponent, + InputIntegerComponent, + InputFloatComponent, InputTextComponent, - BooleanComponent + BooleanComponent, ], - providers: [AuthService, AuthGuard, + providers: [ + AppConfigService, + AuthService, + AuthGuard, { - provide: HTTP_INTERCEPTORS, - useClass: InterceptorService, - multi: true - } - ] + provide: HTTP_INTERCEPTORS, + useClass: InterceptorService, + multi: true, + }, + ], }) export class AppRoutingModule { constructor(public translate: TranslateService) { - translate.setDefaultLang('en'); - translate.use('en'); + translate.setDefaultLang("en"); + translate.use("en"); } -} \ No newline at end of file +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 141352b..0b7b98b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,25 +1,32 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +import { APP_INITIALIZER, NgModule } from "@angular/core"; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { HeaderComponent } from './components/header/header.component'; -import { FooterComponent } from './components/footer/footer.component'; +import { AppComponent } from "./app.component"; +import { AppConfigService } from "./services/config.service"; +import { AppRoutingModule } from "./app-routing.module"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { BrowserModule } from "@angular/platform-browser"; +import { FooterComponent } from "./components/footer/footer.component"; +import { HeaderComponent } from "./components/header/header.component"; + +export function initConfigService(appConfig: AppConfigService) { + return (): Promise => { + return appConfig.loadConfig(); + }; +} @NgModule({ - declarations: [ - AppComponent, - HeaderComponent, - FooterComponent - ], - imports: [ - BrowserModule, - AppRoutingModule, - BrowserAnimationsModule + declarations: [AppComponent, HeaderComponent, FooterComponent], + imports: [BrowserModule, AppRoutingModule, BrowserAnimationsModule], + + providers: [ + AppConfigService, + { + provide: APP_INITIALIZER, + useFactory: initConfigService, + deps: [AppConfigService], + multi: true, + }, ], - - providers: [], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) -export class AppModule { } +export class AppModule {} diff --git a/src/app/auth.guard.ts b/src/app/auth.guard.ts index 5b8956f..0b95d9e 100644 --- a/src/app/auth.guard.ts +++ b/src/app/auth.guard.ts @@ -1,22 +1,20 @@ -import { Injectable } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; -import { AuthService } from './services/auth.service'; +import { CanActivate, Router } from "@angular/router"; + +import { AuthService } from "./services/auth.service"; +import { Injectable } from "@angular/core"; @Injectable({ - providedIn: 'root' + providedIn: "root", }) export class AuthGuard implements CanActivate { - constructor(private _authService: AuthService, private _router: Router) { - - } + constructor(private _authService: AuthService, private _router: Router) {} canActivate(): boolean { if (this._authService.getAuthorizationToken()) { return true; } else { - this._router.navigate(['']); + this._router.navigate([""]); return false; } } - } diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 5679ee7..4c21d7a 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -1,21 +1,30 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse, HttpHeaders } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { environment } from '../../environments/environment.prod'; +import { HttpClient, HttpHeaders, HttpResponse } from "@angular/common/http"; + +import { AppConfigService } from "../services/config.service"; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; @Injectable() export class AuthService { - apiUrl = environment.apiBaseUrl; - clientID = environment.clientId; + constructor( + private httpClient: HttpClient, + private config: AppConfigService + ) {} + + get apiUrl() { + return this.config.getConfig()["apiBaseUrl"]; + } - constructor(private httpClient: HttpClient) { } + get clientID() { + return this.config.getConfig()["clientId"]; + } public getAuthorizationToken(): string { - return localStorage.getItem('accessToken'); + return localStorage.getItem("accessToken"); } public getRefreshToken(): string { - return localStorage.getItem('refreshToken'); + return localStorage.getItem("refreshToken"); } refreshToken(): Observable> { diff --git a/src/app/services/config.service.spec.ts b/src/app/services/config.service.spec.ts new file mode 100644 index 0000000..9685ab4 --- /dev/null +++ b/src/app/services/config.service.spec.ts @@ -0,0 +1,11 @@ +import { AppConfigService } from './config.service'; +import { TestBed } from '@angular/core/testing'; + +describe('AppConfigService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: AppConfigService = TestBed.get(AppConfigService); + expect(service).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/src/app/services/config.service.ts b/src/app/services/config.service.ts new file mode 100644 index 0000000..95ed9ac --- /dev/null +++ b/src/app/services/config.service.ts @@ -0,0 +1,32 @@ +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { environment } from "../../environments/environment"; + +@Injectable({ + providedIn: "root", +}) +export class AppConfigService { + private config: any = { ...environment }; + constructor(private http: HttpClient) {} + + public async loadConfig() { + if ("configFile" in environment) { + const data = await this.http + .get(environment["configFile"]) + .toPromise() + .then((config) => { + this.config = Object.assign({}, environment, config); + console.debug( + "Configuration updated from " + environment["configFile"] + ); + }) + .catch((reason) => { + console.warn("Unable to load configuration from server", reason); + }); + } + } + + public getConfig() { + return this.config; + } +} diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 8f864a8..a137028 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -1,7 +1,8 @@ +import { HttpClient, HttpResponse } from '@angular/common/http'; + +import { AppConfigService } from './config.service'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { environment } from '../../environments/environment.prod'; import { Weight } from '../components/power-user/power-user.component'; @Injectable({ @@ -9,9 +10,16 @@ import { Weight } from '../components/power-user/power-user.component'; }) export class DataService { - apiUrl = environment.apiBaseUrl; - constructor(private httpClient: HttpClient) { } + constructor( + private httpClient: HttpClient, + private config: AppConfigService + ) {} + + get apiUrl() { + return this.config.getConfig()["apiBaseUrl"]; + } + allowedProperties(modelId, propertyId, datasetId): Observable> { let url = this.apiUrl.concat("/allowedProperties?model_id="); diff --git a/src/app/services/interceptor.service.ts b/src/app/services/interceptor.service.ts index 1cd41e4..de8a920 100644 --- a/src/app/services/interceptor.service.ts +++ b/src/app/services/interceptor.service.ts @@ -1,12 +1,13 @@ -import { Injectable } from '@angular/core'; -import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http'; -import { AuthService } from './auth.service'; -import { throwError, empty } from 'rxjs'; +import { HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { MatDialog, MatDialogConfig } from '@angular/material'; import { catchError, tap } from 'rxjs/operators'; +import { empty, throwError } from 'rxjs'; + +import { AuthService } from './auth.service'; +import { ConfirmDialogComponent } from '../components/confirm-dialog/confirm-dialog.component'; +import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { MatDialogConfig, MatDialog } from '@angular/material'; import { TranslateService } from '@ngx-translate/core'; -import { ConfirmDialogComponent } from '../components/confirm-dialog/confirm-dialog.component'; @Injectable() export class InterceptorService implements HttpInterceptor { diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts index d8757eb..6ac8ea4 100644 --- a/src/environments/environment.dev.ts +++ b/src/environments/environment.dev.ts @@ -1,5 +1,12 @@ export const environment = { - production: false, - name: 'dev', - apiBaseUrl: 'http://localhost:8089/wrapper-backend' + production: false, + name: "dev", + apiBaseUrl: "http://localhost:8089/wrapper-backend", + clientId: "", + // 'configFile' is the path of a JSON resource containing + // the configuration properties that should be loaded + // and set without rebuild (e.g., apiBaseUrl, clientId). + // They overwrite the default environment settings defined at build-time + // and are exposed by the `AppConfigService.getConfig()` method. + // configFile: "/assets/config.json", }; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index da6dccd..2b55463 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,5 +1,11 @@ export const environment = { production: true, - apiBaseUrl: 'https://jenkins-master-deephealth-unix01.ing.unimore.it/backend_dev', - clientId: 'BnEOyrMrJ9qeMHeZO7lzF7kgpIFBLm2ZtBd8achT' + apiBaseUrl: "https://jenkins-master-deephealth-unix01.ing.unimore.it/backend", + clientId: "BnEOyrMrJ9qeMHeZO7lzF7kgpIFBLm2ZtBd8achT", + // 'configFile' is the path of a JSON resource containing + // the configuration properties that should be loaded + // and set without rebuild (e.g., apiBaseUrl, clientId). + // They overwrite the default environment settings defined at build-time + // and are exposed by the `AppConfigService.getConfig()` method. + // configFile: "/assets/config.json", }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 880958b..4579108 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,8 +4,16 @@ export const environment = { production: false, - // apiBaseUrl: 'http://localhost:5000' - apiBaseUrl: 'https://jenkins-master-deephealth-unix01.ing.unimore.it/backend' + // Base URL of the back-end server + apiBaseUrl: "http://localhost:8000/backend", + // OAuth Client ID that you can get registering the front-end at /backend/auth/applications/ + clientId: "", + // 'configFile' is the path of a JSON resource containing + // the configuration properties that should be loaded + // and set without rebuild (e.g., apiBaseUrl, clientId). + // They overwrite the default environment settings defined at build-time + // and are exposed by the `AppConfigService.getConfig()` method. + configFile: "/assets/config.json", }; /*