Skip to content
Snippets Groups Projects
binary-file.service.ts 7.53 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den
    
     * Ministerpräsidenten des Landes Schleswig-Holstein
     * Staatskanzlei
     * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
     *
     * Lizenziert unter der EUPL, Version 1.2 oder - sobald
     * diese von der Europäischen Kommission genehmigt wurden -
     * Folgeversionen der EUPL ("Lizenz");
     * Sie dürfen dieses Werk ausschließlich gemäß
     * dieser Lizenz nutzen.
     * Eine Kopie der Lizenz finden Sie hier:
     *
     * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
     *
     * Sofern nicht durch anwendbare Rechtsvorschriften
     * gefordert oder in schriftlicher Form vereinbart, wird
     * die unter der Lizenz verbreitete Software "so wie sie
     * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
     * ausdrücklich oder stillschweigend - verbreitet.
     * Die sprachspezifischen Genehmigungen und Beschränkungen
     * unter der Lizenz sind dem Lizenztext zu entnehmen.
     */
    
    Sebastian Bergandy's avatar
    Sebastian Bergandy committed
    import { BlobWithFileName, createEmptyStateResource, createErrorStateResource, createStateResource, EMPTY_ARRAY, EMPTY_STRING, getMessageForInvalidParam, HttpError, HttpHeader, isNotNil, isUnprocessableEntity, isValidationFieldFileSizeExceedError, sanitizeFileName, StateResource, } from '@alfa-client/tech-shared';
    
    import { SnackBarService } from '@alfa-client/ui';
    
    import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    
    Sebastian Bergandy's avatar
    Sebastian Bergandy committed
    import { faker } from '@faker-js/faker';
    
    import { getUrl, Resource, ResourceUri } from '@ngxp/rest';
    
    import { saveAs } from 'file-saver';
    import { isNil } from 'lodash-es';
    
    import { BehaviorSubject, forkJoin, Observable, of, throwError } from 'rxjs';
    import { catchError, map, mergeMap, startWith, switchMap } from 'rxjs/operators';
    
    Sebastian Bergandy's avatar
    Sebastian Bergandy committed
    import { BinaryFileListResource, BinaryFileResource, FileUploadType, ToUploadFile, UploadFile, UploadFilesByType, } from './binary-file.model';
    
    import { BinaryFileRepository } from './binary-file.repository';
    
    @Injectable({ providedIn: 'root' })
    export class BinaryFileService {
    
      uploadFiles: BehaviorSubject<UploadFilesByType> = new BehaviorSubject({});
    
      constructor(
        private repository: BinaryFileRepository,
        private snackbarService: SnackBarService,
      ) {}
    
      public isUploadInProgress(type: FileUploadType): Observable<boolean> {
    
        return this.uploadFiles.asObservable().pipe(
          map((files: UploadFilesByType) => files[type].map((file: UploadFile) => file.uploadedFile)),
          switchMap((files: Observable<StateResource<BinaryFileResource>>[]) => forkJoin(files)),
          map((files: StateResource<BinaryFileResource>[]) => files.some((s) => s.loading)),
        );
    
      public getUploadedFiles(type: FileUploadType): Observable<UploadFile[]> {
    
        return this.uploadFiles.asObservable().pipe(map((files: UploadFilesByType) => files[type]));
    
      public deleteUploadedFile(type: FileUploadType, key: string): void {
        //Implement me
      }
    
    
      public clearUploadedFiles(type: FileUploadType): void {
        //Implement me
      }
    
    
      //TODO Rename
    
      public uploadFileNew(toUploadFile: ToUploadFile): void {
    
        if (!(toUploadFile.uploadUrl in this.uploadFiles.value)) this.uploadFiles.value[toUploadFile.type] = EMPTY_ARRAY;
    
    Martin's avatar
    Martin committed
        this.uploadFiles.value[toUploadFile.type].push(this._buildUploadFile(toUploadFile));
      }
    
      _buildUploadFile(toUploadFile: ToUploadFile): UploadFile {
        return {
    
          key: faker.string.uuid(),
    
    Martin's avatar
    Martin committed
          fileToUpload: toUploadFile.file,
          uploadedFile: this._handleUpload(toUploadFile),
        };
      }
    
      _handleUpload(toUploadFile: ToUploadFile): Observable<StateResource<BinaryFileResource>> {
    
        return this.repository.uploadFileNew(toUploadFile.uploadUrl, toUploadFile.file).pipe(
    
    Martin's avatar
    Martin committed
          map((binaryFileResource: BinaryFileResource) => createStateResource(binaryFileResource)),
    
          catchError((response: any) => of(createErrorStateResource(<HttpError>response.error))),
    
    Martin's avatar
    Martin committed
          startWith(createEmptyStateResource<BinaryFileResource>(true)),
        );
    
      }
    
      //TODO Rename to uploadFileOld OR refactor all use cases to uploadFileNew
    
      public uploadFile(
        resource: Resource,
        linkRel: string,
        file: File,
    
        showValidationErrorSnackBar: boolean = true,
    
      ): Observable<StateResource<BinaryFileResource>> {
    
        return this.repository.uploadFile(getUrl(resource, linkRel), file).pipe(
    
          mergeMap((response: HttpResponse<Object>) => this.getFile(response.headers.get(HttpHeader.LOCATION))),
          catchError((errorResponse) => this.handleError(errorResponse.error, showValidationErrorSnackBar)),
    
          startWith(createEmptyStateResource<BinaryFileResource>(true)),
        );
      }
    
      private handleError(errorResponse: HttpErrorResponse, showValidationErrorSnackBar: boolean): Observable<StateResource<any>> {
    
        return of(this.handleErrorByStatus(errorResponse, showValidationErrorSnackBar));
    
      handleErrorByStatus(error: HttpErrorResponse, showValidationErrorSnackBar: boolean): StateResource<any> {
    
        if (isUnprocessableEntity(error.status)) {
    
          this.handleSnackBar(error, showValidationErrorSnackBar);
    
          return createErrorStateResource(error.error);
        }
        throwError({ error });
      }
    
      handleSnackBar(error: HttpErrorResponse, showValidationErrorSnackBar: boolean) {
    
        if (showValidationErrorSnackBar && isValidationFieldFileSizeExceedError(error.error)) {
    
          this.snackbarService.showError(getMessageForInvalidParam(EMPTY_STRING, error.error.invalidParams[0]));
    
    Albert Bruns's avatar
    Albert Bruns committed
      public downloadFile(file: BinaryFileResource, fileNamePrefix: string): Observable<StateResource<Blob>> {
    
        return this.repository.download(file).pipe(
          map((data) => this.saveBinaryFile(data, file, fileNamePrefix)),
    
    Albert Bruns's avatar
    Albert Bruns committed
          startWith(createEmptyStateResource<Blob>(true)),
    
          catchError(() => this.handleDownloadError()),
    
    Albert Bruns's avatar
    Albert Bruns committed
      handleDownloadError(): Observable<StateResource<Blob>> {
    
        this.snackbarService.showError('Die Datei konnte nicht heruntergeladen werden.');
    
    Albert Bruns's avatar
    Albert Bruns committed
        return of(createEmptyStateResource<Blob>());
    
      saveBinaryFile(data: any, file: BinaryFileResource, fileNamePrefix: string): StateResource<Blob> {
        if (isNil(data)) {
          return createEmptyStateResource(true);
        }
        this.save(data, this.buildFileName(file, fileNamePrefix));
        return createStateResource(data);
      }
    
      private buildFileName(file: BinaryFileResource, fileNamePrefix: string): string {
        if (isNotNil(fileNamePrefix)) {
          return sanitizeFileName(`${fileNamePrefix}_${file.name}`);
        }
        return file.name;
      }
    
      public downloadArchive(uri: ResourceUri): Observable<StateResource<Blob>> {
    
        return this.repository.downloadArchive(uri).pipe(
          map((data: BlobWithFileName) => this.saveData(data)),
          startWith(createEmptyStateResource<Blob>(true)),
        );
    
      }
    
      saveData(dataWithFileName: BlobWithFileName): StateResource<Blob> {
        const data: Blob = dataWithFileName.blob;
        if (isNil(data)) {
          return createEmptyStateResource(true);
        }
        this.save(data, dataWithFileName.fileName);
        return createStateResource(data);
      }
    
    
      save(data: any, fileName: string): void {
        saveAs(data, fileName);
      }
    
      public getFile(uri: ResourceUri): Observable<StateResource<BinaryFileResource>> {
    
        return this.repository.getFile(uri).pipe(
    
          map((fileList: BinaryFileResource) => createStateResource(fileList)),
    
          startWith(createEmptyStateResource<BinaryFileResource>(true)),
    
      public getFiles(resource: Resource, linkRel: string): Observable<StateResource<BinaryFileListResource>> {
    
        return this.repository.getFiles(resource, linkRel).pipe(
    
          map((fileList: BinaryFileListResource) => createStateResource(fileList)),
    
          startWith(createEmptyStateResource<BinaryFileListResource>(true)),
        );
      }
    }