From b799600b7345e6adf07fa784810e3f57abc91ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 1 Sep 2021 12:14:11 +0200 Subject: [PATCH] feat(config)!: update config file for better iiif support (DSP-1880) (#511) * chore(config)!: consistent config * refactor(app): move app init service to app root * refactor(representation): upload service was already there * feat(upload)!: new iiif url config --- .../services => }/app-init.service.spec.ts | 0 src/app/app-init.service.ts | 86 +++++++++++++++ src/app/app.module.ts | 2 +- src/app/main/declarations/dsp-iiif-config.ts | 44 ++++++++ src/app/main/services/app-init.service.ts | 67 ------------ .../upload/upload-file.service.spec.ts | 7 +- .../upload/upload-file.service.ts | 10 +- .../resource/services/geoname.service.spec.ts | 2 +- .../resource/services/geoname.service.ts | 2 +- .../services/upload-file.service.spec.ts | 100 ------------------ .../resource/services/upload-file.service.ts | 46 -------- src/config/config.dev.json | 9 +- src/config/config.prod.json | 9 +- src/config/config.test-server.json | 9 +- 14 files changed, 161 insertions(+), 232 deletions(-) rename src/app/{main/services => }/app-init.service.spec.ts (100%) create mode 100644 src/app/app-init.service.ts create mode 100644 src/app/main/declarations/dsp-iiif-config.ts delete mode 100644 src/app/main/services/app-init.service.ts delete mode 100644 src/app/workspace/resource/services/upload-file.service.spec.ts delete mode 100644 src/app/workspace/resource/services/upload-file.service.ts diff --git a/src/app/main/services/app-init.service.spec.ts b/src/app/app-init.service.spec.ts similarity index 100% rename from src/app/main/services/app-init.service.spec.ts rename to src/app/app-init.service.spec.ts diff --git a/src/app/app-init.service.ts b/src/app/app-init.service.ts new file mode 100644 index 0000000000..221b370735 --- /dev/null +++ b/src/app/app-init.service.ts @@ -0,0 +1,86 @@ +import { Injectable } from '@angular/core'; +import { KnoraApiConfig } from '@dasch-swiss/dsp-js'; +import { DspIiifConfig } from './main/declarations/dsp-iiif-config'; + +@Injectable({ + providedIn: 'root' +}) +export class AppInitService { + + dspApiConfig: KnoraApiConfig; + dspIiifConfig: DspIiifConfig; + + config: object; + + constructor() { + } + + /** + * fetches and initialises the configuration. + * + * @param path path to the config file. + * @param env environment to be used (dev or prod). + */ + Init(path: string, env: { name: string; production: boolean }): Promise { + + return new Promise((resolve, reject) => { + fetch(`${path}/config.${env.name}.json`).then( + (response: Response) => response.json()).then(dspAppConfig => { + + + // check for presence of apiProtocol and apiHost + if (typeof dspAppConfig.apiProtocol !== 'string' || typeof dspAppConfig.apiHost !== 'string') { + throw new Error('config misses required members: apiProtocol and/or apiHost'); + } + + // make input type safe + const apiPort = (typeof dspAppConfig.apiPort === 'number' ? dspAppConfig.apiPort : null); + const apiPath = (typeof dspAppConfig.apiPath === 'string' ? dspAppConfig.apiPath : ''); + const jsonWebToken = (typeof dspAppConfig.jsonWebToken === 'string' ? dspAppConfig.jsonWebToken : ''); + const logErrors = (typeof dspAppConfig.logErrors === 'boolean' ? dspAppConfig.logErrors : false); + + // init dsp-api configuration + this.dspApiConfig = new KnoraApiConfig( + dspAppConfig.apiProtocol, + dspAppConfig.apiHost, + apiPort, + apiPath, + jsonWebToken, + logErrors + ); + + const iiifPort = (typeof dspAppConfig.iiifPort === 'number' ? dspAppConfig.iiifPort : null); + const iiifPath = (typeof dspAppConfig.iiifPath === 'string' ? dspAppConfig.iiifPath : ''); + + // init iiif configuration + this.dspIiifConfig = new DspIiifConfig( + dspAppConfig.iiifProtocol, + dspAppConfig.iiifHost, + iiifPort, + iiifPath + ); + + // get all options from config + this.config = dspAppConfig; + + // set sanitized standard config options + this.config['apiProtocol'] = dspAppConfig.apiProtocol; + this.config['apiHost'] = dspAppConfig.apiHost; + this.config['apiPort'] = apiPort; + this.config['apiPath'] = apiPath; + this.config['jsonWebToken'] = jsonWebToken; + this.config['logErrors'] = logErrors; + this.config['iiifProtocol'] = dspAppConfig.iiifProtocol; + this.config['iiifHost'] = dspAppConfig.iiifHost; + this.config['iiifPort'] = iiifPort; + this.config['iiifPath'] = iiifPath; + this.config['iiifUrl'] = this.dspIiifConfig.iiifUrl; + + resolve(); + } + ).catch((err) => { + reject(err); + }); + }); + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5c32a65511..2cef8761c9 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,7 +8,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { KnoraApiConnection } from '@dasch-swiss/dsp-js'; import { - AppInitService, DspActionModule, DspApiConfigToken, DspApiConnectionToken, @@ -23,6 +22,7 @@ import { PdfViewerModule } from 'ng2-pdf-viewer'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { AppInitService } from './app-init.service'; import { CookiePolicyComponent } from './main/cookie-policy/cookie-policy.component'; import { DialogHeaderComponent } from './main/dialog/dialog-header/dialog-header.component'; import { DialogComponent } from './main/dialog/dialog.component'; diff --git a/src/app/main/declarations/dsp-iiif-config.ts b/src/app/main/declarations/dsp-iiif-config.ts new file mode 100644 index 0000000000..ed8e80eebd --- /dev/null +++ b/src/app/main/declarations/dsp-iiif-config.ts @@ -0,0 +1,44 @@ +/** + * configuration to instantiate the iiif url. + * + * @category Config + */ +export class DspIiifConfig { + + static readonly PROTOCOL_HTTP = 'http'; + static readonly PROTOCOL_HTTPS = 'https'; + + static readonly DEFAULT_PORT_HTTP = 80; + static readonly DEFAULT_PORT_HTTPS = 443; + + /** + * the full IIIF URL + */ + get iiifUrl(): string { + return ( + (this.iiifProtocol + '://' + this.iiifHost) + + (this.iiifPort !== null ? ':' + this.iiifPort : '') + + this.iiifPath + ); + } + + /** + * @param iiifProtocol the protocol of the API (http or https) + * @param iiifHost the DSP-API base URL + * @param iiifPort the port of DSP-API + * @param iiifPath the base path following host and port, if any. + */ + constructor(public iiifProtocol: 'http' | 'https', + public iiifHost: string, + public iiifPort: number | null = null, + public iiifPath: string = '') { + + // remove port in case it's the default one + if (iiifProtocol === DspIiifConfig.PROTOCOL_HTTP && iiifPort === DspIiifConfig.DEFAULT_PORT_HTTP) { + this.iiifPort = null; + } else if (iiifProtocol === DspIiifConfig.PROTOCOL_HTTPS && iiifPort === DspIiifConfig.DEFAULT_PORT_HTTPS) { + this.iiifPort = null; + } + + } +} diff --git a/src/app/main/services/app-init.service.ts b/src/app/main/services/app-init.service.ts deleted file mode 100644 index a19cc804f4..0000000000 --- a/src/app/main/services/app-init.service.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Injectable } from '@angular/core'; -import { KnoraApiConfig } from '@dasch-swiss/dsp-js'; - -@Injectable({ - providedIn: 'root' -}) -export class AppInitService { - - dspApiConfig: KnoraApiConfig; - - config: object; - - constructor() { - } - - /** - * fetches and initialises the configuration. - * - * @param path path to the config file. - * @param env environment to be used (dev or prod). - */ - Init(path: string, env: { name: string; production: boolean }): Promise { - - return new Promise((resolve, reject) => { - fetch(`${path}/config.${env.name}.json`).then( - (response: Response) => response.json()).then(dspApiConfig => { - - // check for presence of apiProtocol and apiHost - if (typeof dspApiConfig.apiProtocol !== 'string' || typeof dspApiConfig.apiHost !== 'string') { - throw new Error('config misses required members: apiProtocol and/or apiHost'); - } - - // make input type safe - const apiPort = (typeof dspApiConfig.apiPort === 'number' ? dspApiConfig.apiPort : null); - const apiPath = (typeof dspApiConfig.apiPath === 'string' ? dspApiConfig.apiPath : ''); - const jsonWebToken = (typeof dspApiConfig.jsonWebToken === 'string' ? dspApiConfig.jsonWebToken : ''); - const logErrors = (typeof dspApiConfig.logErrors === 'boolean' ? dspApiConfig.logErrors : false); - - // init dsp-api configuration - this.dspApiConfig = new KnoraApiConfig( - dspApiConfig.apiProtocol, - dspApiConfig.apiHost, - apiPort, - apiPath, - jsonWebToken, - logErrors - ); - - // get all options from config - this.config = dspApiConfig; - - // set sanitized standard config options - this.config['apiProtocol'] = dspApiConfig.apiProtocol; - this.config['apiHost'] = dspApiConfig.apiHost; - this.config['apiPort'] = apiPort; - this.config['apiPath'] = apiPath; - this.config['jsonWebToken'] = jsonWebToken; - this.config['logErrors'] = logErrors; - - resolve(); - } - ).catch((err) => { - reject(err); - }); - }); - } -} diff --git a/src/app/workspace/resource/representation/upload/upload-file.service.spec.ts b/src/app/workspace/resource/representation/upload/upload-file.service.spec.ts index c521c389b4..dd453cea51 100644 --- a/src/app/workspace/resource/representation/upload/upload-file.service.spec.ts +++ b/src/app/workspace/resource/representation/upload/upload-file.service.spec.ts @@ -1,6 +1,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { AppInitService, Session, SessionService } from '@dasch-swiss/dsp-ui'; +import { Session, SessionService } from '@dasch-swiss/dsp-ui'; +import { AppInitService } from 'src/app/app-init.service'; import { UploadedFileResponse, UploadFileService } from './upload-file.service'; describe('UploadFileService', () => { @@ -15,7 +16,7 @@ describe('UploadFileService', () => { const appInitSpy = { config: { - iiifUrl: 'https://sipi.dasch.swiss/' + 'iiifUrl': 'https://iiif.dasch.swiss' } }; @@ -82,7 +83,7 @@ describe('UploadFileService', () => { } ); - const httpRequest = httpTestingController.expectOne('https://sipi.dasch.swiss/upload?token=myToken'); + const httpRequest = httpTestingController.expectOne('https://iiif.dasch.swiss/upload?token=myToken'); expect(httpRequest.request.method).toEqual('POST'); diff --git a/src/app/workspace/resource/representation/upload/upload-file.service.ts b/src/app/workspace/resource/representation/upload/upload-file.service.ts index 278795ada8..5864621bb0 100644 --- a/src/app/workspace/resource/representation/upload/upload-file.service.ts +++ b/src/app/workspace/resource/representation/upload/upload-file.service.ts @@ -1,7 +1,8 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { AppInitService, SessionService } from '@dasch-swiss/dsp-ui'; +import { SessionService } from '@dasch-swiss/dsp-ui'; import { Observable } from 'rxjs'; +import { AppInitService } from 'src/app/app-init.service'; export interface UploadedFile { fileType: string; @@ -19,7 +20,7 @@ export interface UploadedFileResponse { }) export class UploadFileService { - iiifHost: string = (this._init.config['iiifUrl'].substr(-1) === '/') ? this._init.config['iiifUrl'] : this._init.config['iiifUrl'] + '/'; + iiifUrl: string = (this._init.config['iiifUrl'].substr(-1) === '/') ? this._init.config['iiifUrl'] : this._init.config['iiifUrl'] + '/'; constructor( private readonly _init: AppInitService, @@ -32,7 +33,8 @@ export class UploadFileService { * @param (file) */ upload(file: FormData): Observable { - const baseUrl = `${this.iiifHost}upload`; + + const uploadUrl = `${this.iiifUrl}upload`; // checks if user is logged in const jwt = this._session.getSession()?.user.jwt; @@ -40,6 +42,6 @@ export class UploadFileService { // --> TODO in order to track the progress change below to true and 'events' const options = { params, reportProgress: false, observe: 'body' as 'body' }; - return this._http.post(baseUrl, file, options); + return this._http.post(uploadUrl, file, options); } } diff --git a/src/app/workspace/resource/services/geoname.service.spec.ts b/src/app/workspace/resource/services/geoname.service.spec.ts index 9bdc18cb6e..8e4fa00119 100644 --- a/src/app/workspace/resource/services/geoname.service.spec.ts +++ b/src/app/workspace/resource/services/geoname.service.spec.ts @@ -1,6 +1,6 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { AppInitService } from 'src/app/main/services/app-init.service'; +import { AppInitService } from 'src/app/app-init.service'; import { DisplayPlace, GeonameService } from './geoname.service'; const geonamesGetResponse = { diff --git a/src/app/workspace/resource/services/geoname.service.ts b/src/app/workspace/resource/services/geoname.service.ts index 7f63c97d46..a106506155 100644 --- a/src/app/workspace/resource/services/geoname.service.ts +++ b/src/app/workspace/resource/services/geoname.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, throwError } from 'rxjs'; import { catchError, map, shareReplay } from 'rxjs/operators'; -import { AppInitService } from 'src/app/main/services/app-init.service'; +import { AppInitService } from 'src/app/app-init.service'; export interface GIS { longitude: number; diff --git a/src/app/workspace/resource/services/upload-file.service.spec.ts b/src/app/workspace/resource/services/upload-file.service.spec.ts deleted file mode 100644 index 1e09789a5a..0000000000 --- a/src/app/workspace/resource/services/upload-file.service.spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { TestBed } from '@angular/core/testing'; -import { AppInitService } from 'src/app/main/services/app-init.service'; -import { Session, SessionService } from 'src/app/main/services/session.service'; -import { UploadedFileResponse, UploadFileService } from './upload-file.service'; - -describe('UploadFileService', () => { - let service: UploadFileService; - let httpTestingController: HttpTestingController; - - const file = new File(['1'], 'testfile'); - const mockUploadData = new FormData(); - mockUploadData.append('test', file); - - beforeEach(() => { - - const appInitSpy = { - config: { - sipiUrl: 'https://sipi.dasch.swiss/' - } - }; - - const sessionSpy = jasmine.createSpyObj('SessionService', ['getSession']); - - TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule - ], - providers: [ - { provide: AppInitService, useValue: appInitSpy }, - { provide: SessionService, useValue: sessionSpy }, - ] - }); - - service = TestBed.inject(UploadFileService); - httpTestingController = TestBed.inject(HttpTestingController); - - const sessionServiceSpy = TestBed.inject(SessionService) as jasmine.SpyObj; - - sessionServiceSpy.getSession.and.callFake( - () => { - const session: Session = { - id: 12345, - user: { - name: 'username', - jwt: 'myToken', - lang: 'en', - sysAdmin: false, - projectAdmin: [] - } - }; - - return session; - } - ); - }); - - afterEach(() => { - // after every test, assert that there are no more pending requests. - httpTestingController.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - it('should return expected file response on upload', done => { - - const expectedResponse: UploadedFileResponse = { - uploadedFiles: [{ - fileType: 'image', - internalFilename: '8R0cJE3TSgB-BssuQyeW1rE.jp2', - originalFilename: 'Screenshot 2020-10-28 at 14.16.34.png', - temporaryUrl: 'http://sipi:1024/tmp/8R0cJE3TSgB-BssuQyeW1rE.jp2' - }] - }; - - service.upload(mockUploadData).subscribe( - res => { - expect(res.uploadedFiles.length).toEqual(1); - expect(res.uploadedFiles[0].internalFilename).toEqual('8R0cJE3TSgB-BssuQyeW1rE.jp2'); - done(); - } - ); - - const httpRequest = httpTestingController.expectOne('https://sipi.dasch.swiss/upload?token=myToken'); - - expect(httpRequest.request.method).toEqual('POST'); - - const expectedFormData = new FormData(); - const mockFile = new File(['1'], 'testfile', { type: 'image/jpeg' }); - - expectedFormData.append(mockFile.name, mockFile); - expect(httpRequest.request.body).toEqual(expectedFormData); - - httpRequest.flush(expectedResponse); - - }); - -}); diff --git a/src/app/workspace/resource/services/upload-file.service.ts b/src/app/workspace/resource/services/upload-file.service.ts deleted file mode 100644 index f80d983f5d..0000000000 --- a/src/app/workspace/resource/services/upload-file.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { HttpClient, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { AppInitService } from 'src/app/main/services/app-init.service'; -import { SessionService } from 'src/app/main/services/session.service'; - -interface UploadedFile { - fileType: string; - internalFilename: string; - originalFilename: string; - temporaryUrl: string; -} - -export interface UploadedFileResponse { - uploadedFiles: UploadedFile[]; -} - -@Injectable({ - providedIn: 'root' -}) -export class UploadFileService { - - sipiHost: string = this._is.config['sipiUrl']; - - constructor( - private readonly _is: AppInitService, - private readonly _http: HttpClient, - private readonly _ss: SessionService - ) { } - - /** - * uploads files to SIPI - * @param (file) - */ - upload(file: FormData): Observable { - const baseUrl = `${this.sipiHost}upload`; - - // checks if user is logged in - const jwt = this._ss.getSession()?.user.jwt; - const params = new HttpParams().set('token', jwt); - - // tODO in order to track the progress change below to true and 'events' - const options = { params, reportProgress: false, observe: 'body' as 'body' }; - return this._http.post(baseUrl, file, options); - } -} diff --git a/src/config/config.dev.json b/src/config/config.dev.json index cb1761f4d9..510606c81f 100644 --- a/src/config/config.dev.json +++ b/src/config/config.dev.json @@ -3,8 +3,11 @@ "apiHost": "0.0.0.0", "apiPort": 3333, "apiPath": "", + "iiifProtocol": "http", + "iiifHost": "0.0.0.0", + "iiifPort": 1024, + "iiifPath": "", + "geonameToken": "knora", "jsonWebToken": "", - "logErrors": true, - "iiifUrl": "http://localhost:1024", - "geonameToken": "knora" + "logErrors": true } diff --git a/src/config/config.prod.json b/src/config/config.prod.json index 3350fadafc..a91c65be85 100644 --- a/src/config/config.prod.json +++ b/src/config/config.prod.json @@ -3,8 +3,11 @@ "apiHost": "api.dasch.swiss", "apiPort": 443, "apiPath": "", + "iiifProtocol": "https", + "iiifHost": "iiif.dasch.swiss", + "iiifPort": 443, + "iiifPath": "", + "geonameToken": "knora", "jsonWebToken": "", - "logErrors": false, - "iiifUrl": "https://iiif.dasch.swiss", - "geonameToken": "knora" + "logErrors": false } diff --git a/src/config/config.test-server.json b/src/config/config.test-server.json index 2817623045..a211a98595 100644 --- a/src/config/config.test-server.json +++ b/src/config/config.test-server.json @@ -3,8 +3,11 @@ "apiHost": "api.test.dasch.swiss", "apiPort": 443, "apiPath": "", + "iiifProtocol": "https", + "iiifHost": "iiif.test.dasch.swiss", + "iiifPort": 443, + "iiifPath": "", + "geonameToken": "knora", "jsonWebToken": "", - "logErrors": true, - "iiifUrl": "https://iiif.test.dasch.swiss", - "geonameToken": "knora" + "logErrors": true }