update file download service

This commit is contained in:
Dan Percic 2021-10-07 14:14:07 +03:00
parent 0ca1c3b536
commit 5365c6c7e6
12 changed files with 154 additions and 147 deletions

View File

@ -14,5 +14,9 @@ max_line_length = 140
max_line_length = off
trim_trailing_whitespace = false
[*.ts]
ij_typescript_use_double_quotes = false
ij_typescript_enforce_trailing_comma = keep
[{*.json, .prettierrc, .eslintrc}]
indent_size = 2

View File

@ -184,6 +184,7 @@
"prefer-const": "error",
"radix": "error",
"curly": "error",
"quotes": ["error", "single"],
"comma-dangle": [
"error",
{

View File

@ -1,22 +1,34 @@
import { Component, forwardRef, Injector, OnInit } from '@angular/core';
import { Component, forwardRef, Injector } from '@angular/core';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { DownloadStatusWrapper } from '@upload-download/model/download-status.wrapper';
import { DownloadControllerService } from '@redaction/red-ui-http';
import { CircleButtonTypes, DefaultListingServices, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
import { DownloadStatus } from '@upload-download/model/download-status';
import {
CircleButtonTypes,
DefaultListingServicesTmp,
EntitiesService,
ListingComponent,
LoadingService,
TableColumnConfig,
} from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { timer } from 'rxjs';
import { RouterHistoryService } from '@services/router-history.service';
@Component({
selector: 'redaction-downloads-list-screen',
templateUrl: './downloads-list-screen.component.html',
styleUrls: ['./downloads-list-screen.component.scss'],
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DownloadsListScreenComponent) }],
providers: [
...DefaultListingServicesTmp,
{ provide: EntitiesService, useExisting: FileDownloadService },
{
provide: ListingComponent,
useExisting: forwardRef(() => DownloadsListScreenComponent),
},
],
})
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatusWrapper> implements OnInit {
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> {
readonly circleButtonTypes = CircleButtonTypes;
readonly tableHeaderLabel = _('downloads-list.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<DownloadStatusWrapper>[] = [
readonly tableColumnConfigs: TableColumnConfig<DownloadStatus>[] = [
{ label: _('downloads-list.table-col-names.name'), width: '2fr' },
{ label: _('downloads-list.table-col-names.size') },
{ label: _('downloads-list.table-col-names.date') },
@ -28,37 +40,27 @@ export class DownloadsListScreenComponent extends ListingComponent<DownloadStatu
private readonly _loadingService: LoadingService,
readonly fileDownloadService: FileDownloadService,
readonly routerHistoryService: RouterHistoryService,
private readonly _downloadControllerService: DownloadControllerService,
) {
super(_injector);
this._loadingService.loadWhile(this._loadData());
}
async ngOnInit() {
this._loadingService.start();
await this._loadData();
this.addSubscription = timer(0, 5000).subscribe(async () => {
await this._loadData();
});
this._loadingService.stop();
}
downloadItem(download: DownloadStatusWrapper) {
downloadItem(download: DownloadStatus) {
this._loadingService.loadWhile(this.fileDownloadService.performDownload(download));
}
deleteItems(downloads?: DownloadStatusWrapper[]) {
deleteItems(downloads?: DownloadStatus[]) {
this._loadingService.loadWhile(this._deleteItems(downloads));
}
private async _deleteItems(downloads?: DownloadStatusWrapper[]) {
private async _deleteItems(downloads?: DownloadStatus[]) {
const storageIds = (downloads || this.listingService.selected).map(d => d.storageId);
await this._downloadControllerService.deleteDownload({ storageIds }).toPromise();
await this.fileDownloadService.delete({ storageIds }).toPromise();
this.listingService.setSelected([]);
await this._loadData();
}
private async _loadData() {
await this.fileDownloadService.getDownloadStatus().toPromise();
this.entitiesService.setEntities(this.fileDownloadService.downloads);
await this.fileDownloadService.loadAll().toPromise();
}
}

View File

@ -45,7 +45,10 @@ export class FileDownloadBtnComponent extends AutoUnsubscribe implements OnDestr
downloadFiles($event: MouseEvent) {
$event.stopPropagation();
this.addSubscription = this._fileDownloadService
.downloadFiles(this.files, this.dossier)
.downloadFiles(
this.files.map(f => f.fileId),
this.dossier.dossierId,
)
.subscribe(() => this._toaster.info(_('download-status.queued')));
}
}

View File

@ -0,0 +1,50 @@
import { DownloadFileType, DownloadStatusType, IDownloadStatus, List } from '@redaction/red-ui-http';
import { IListable } from '@iqser/common-ui';
export class DownloadStatus implements IDownloadStatus, IListable {
readonly creationDate?: string;
readonly dossierId?: string;
readonly downloadFileTypes?: List<DownloadFileType>;
readonly fileIds?: List;
readonly fileSize?: number;
readonly filename?: string;
readonly lastDownload?: string;
readonly mimeType?: string;
readonly status?: DownloadStatusType;
readonly storageId?: string;
readonly userId?: string;
readonly size?: string;
inProgress: boolean;
constructor(downloadStatus: IDownloadStatus) {
this.creationDate = downloadStatus.creationDate;
this.dossierId = downloadStatus.dossierId;
this.downloadFileTypes = downloadStatus.downloadFileTypes;
this.fileIds = downloadStatus.fileIds;
this.fileSize = downloadStatus.fileSize;
this.filename = downloadStatus.filename;
this.lastDownload = downloadStatus.lastDownload;
this.mimeType = downloadStatus.mimeType;
this.status = downloadStatus.status;
this.storageId = downloadStatus.storageId;
this.userId = downloadStatus.userId;
this.size = this._size;
}
get id() {
return this.storageId;
}
get searchKey(): string {
return this.storageId;
}
get isReady() {
return this.status === 'READY';
}
private get _size() {
const i = this.fileSize === 0 ? 0 : Math.floor(Math.log(this.fileSize) / Math.log(1024));
return (this.fileSize / Math.pow(1024, i)).toFixed(2) + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
}

View File

@ -1,53 +0,0 @@
import { DownloadStatus } from '@redaction/red-ui-http';
import { IListable } from '@iqser/common-ui';
export class DownloadStatusWrapper implements IListable {
inProgress: boolean;
constructor(private _downloadStatus: DownloadStatus) {}
get id() {
return this.storageId;
}
get searchKey(): string {
return this.storageId;
}
get size() {
const i = this._downloadStatus.fileSize === 0 ? 0 : Math.floor(Math.log(this._downloadStatus.fileSize) / Math.log(1024));
return (this._downloadStatus.fileSize / Math.pow(1024, i)).toFixed(2) + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
get creationDate() {
return this._downloadStatus.creationDate;
}
get filename() {
return this._downloadStatus.filename;
}
get lastDownload() {
return this._downloadStatus.lastDownload;
}
get mimeType() {
return this._downloadStatus.mimeType;
}
get dossierId() {
return this._downloadStatus.dossierId;
}
get isReady() {
return this._downloadStatus.status === 'READY';
}
get status() {
return this._downloadStatus.status;
}
get storageId() {
return this._downloadStatus.storageId;
}
}

View File

@ -1,59 +1,57 @@
import { ApplicationRef, Injectable } from '@angular/core';
import { DownloadControllerService, FileManagementControllerService } from '@redaction/red-ui-http';
import { Injectable, Injector } from '@angular/core';
import {
DownloadResponse,
DownloadStatusResponse,
IDownloadStatus,
PrepareDownloadRequest,
RemoveDownloadRequest,
} from '@redaction/red-ui-http';
import { interval, Observable } from 'rxjs';
import { ConfigService } from '@services/config.service';
import { TranslateService } from '@ngx-translate/core';
import { Dossier } from '@state/model/dossier';
import { File } from '@models/file/file';
import { mergeMap, tap } from 'rxjs/operators';
import { DownloadStatusWrapper } from '../model/download-status.wrapper';
import { AppStateService } from '@state/app-state.service';
import { map, mergeMap, tap } from 'rxjs/operators';
import { DownloadStatus } from '../model/download-status';
import { KeycloakService } from 'keycloak-angular';
import { UserService } from '@services/user.service';
import { List } from '@iqser/common-ui';
import { EntitiesService, List, RequiredParam, Validate } from '@iqser/common-ui';
@Injectable()
export class FileDownloadService {
downloads: DownloadStatusWrapper[] = [];
hasPendingDownloads;
export class FileDownloadService extends EntitiesService<DownloadStatus, IDownloadStatus> {
hasPendingDownloads = false;
constructor(
private readonly _userService: UserService,
private readonly _applicationRef: ApplicationRef,
private readonly _keycloakService: KeycloakService,
private readonly _appStateService: AppStateService,
private readonly _translateService: TranslateService,
private readonly _configService: ConfigService,
private readonly _downloadControllerService: DownloadControllerService,
private readonly _fileManagementControllerService: FileManagementControllerService,
protected readonly _injector: Injector,
) {
super(_injector, DownloadStatus, 'async/download');
interval(5000).subscribe(() => {
if (_userService.currentUser.isUser) {
this.getDownloadStatus().subscribe(() => {});
this.loadAll().subscribe(() => {});
}
});
}
downloadFiles(files: List<File>, dossier: Dossier): Observable<any> {
return this._downloadControllerService
.prepareDownload({
fileIds: files.map(f => f.fileId),
dossierId: dossier.id,
})
.pipe(mergeMap(() => this.getDownloadStatus()));
downloadFiles(fileIds: List, dossierId: string): Observable<any> {
return this.prepareDownload({
fileIds,
dossierId,
}).pipe(mergeMap(() => this.loadAll()));
}
getDownloadStatus() {
return this._downloadControllerService.getDownloadStatus().pipe(
tap(statusResponse => {
this.downloads = statusResponse.downloadStatus.map(d => new DownloadStatusWrapper(d));
this.hasPendingDownloads = !!this.downloads.find(f => !f.lastDownload && f.isReady);
}),
loadAll(): Observable<DownloadStatus[]> {
return this.getStatuses().pipe(
map(entities => entities.map(entity => new DownloadStatus(entity))),
tap(entities => this.setEntities(entities)),
tap(() => (this.hasPendingDownloads = !!this.all.find(f => !f.lastDownload && f.isReady))),
);
}
async performDownload(status: DownloadStatusWrapper) {
getStatuses(): Observable<IDownloadStatus[]> {
return super._getOne<DownloadStatusResponse>(['status']).pipe(map(res => res.downloadStatus));
}
async performDownload(status: DownloadStatus) {
const token = await this._keycloakService.getToken();
const anchor = document.createElement('a');
anchor.href = `${this._configService.values.API_URL}/async/download?access_token=${encodeURIComponent(
@ -66,4 +64,14 @@ export class FileDownloadService {
anchor.click();
document.body.removeChild(anchor);
}
@Validate()
prepareDownload(@RequiredParam() body: PrepareDownloadRequest): Observable<DownloadResponse> {
return this._post(body, `${this._defaultModelPath}/prepare`);
}
@Validate()
delete(@RequiredParam() body: RemoveDownloadRequest): Observable<unknown> {
return super._post(body, `${this._defaultModelPath}/delete`);
}
}

View File

@ -1,7 +1,6 @@
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { Configuration } from './configuration';
import { HttpClient } from '@angular/common/http';
import { DownloadControllerService } from './api/downloadController.service';
import { FileManagementControllerService } from './api/fileManagementController.service';
import { GeneralSettingsControllerService } from './api/generalSettingsController.service';
import { InfoControllerService } from './api/infoController.service';
@ -26,7 +25,6 @@ import { StatusReportControllerService } from './api/statusReportController.serv
declarations: [],
exports: [],
providers: [
DownloadControllerService,
FileManagementControllerService,
GeneralSettingsControllerService,
InfoControllerService,

View File

@ -11,5 +11,5 @@
*/
export interface DownloadResponse {
storageId?: string;
readonly storageId?: string;
}

View File

@ -9,35 +9,27 @@
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { DownloadFileType } from './dossier';
import { List } from '../red-types';
export interface DownloadStatus {
creationDate?: string;
dossierId?: string;
downloadFileTypes?: Array<DownloadStatus.DownloadFileTypesEnum>;
fileIds?: Array<string>;
fileSize?: number;
filename?: string;
lastDownload?: string;
mimeType?: string;
status?: DownloadStatus.StatusEnum;
storageId?: string;
userId?: string;
export interface IDownloadStatus {
readonly creationDate?: string;
readonly dossierId?: string;
readonly downloadFileTypes?: List<DownloadFileType>;
readonly fileIds?: List;
readonly fileSize?: number;
readonly filename?: string;
readonly lastDownload?: string;
readonly mimeType?: string;
readonly status?: DownloadStatusType;
readonly storageId?: string;
readonly userId?: string;
}
export namespace DownloadStatus {
export type DownloadFileTypesEnum = 'ANNOTATED' | 'FLATTEN' | 'ORIGINAL' | 'PREVIEW' | 'REDACTED';
export const DownloadFileTypesEnum = {
ANNOTATED: 'ANNOTATED' as DownloadFileTypesEnum,
FLATTEN: 'FLATTEN' as DownloadFileTypesEnum,
ORIGINAL: 'ORIGINAL' as DownloadFileTypesEnum,
PREVIEW: 'PREVIEW' as DownloadFileTypesEnum,
REDACTED: 'REDACTED' as DownloadFileTypesEnum,
};
export type StatusEnum = 'FAILED' | 'GENERATING' | 'QUEUED' | 'READY';
export const StatusEnum = {
FAILED: 'FAILED' as StatusEnum,
GENERATING: 'GENERATING' as StatusEnum,
QUEUED: 'QUEUED' as StatusEnum,
READY: 'READY' as StatusEnum,
};
}
export const DownloadStatuses = {
FAILED: 'FAILED',
GENERATING: 'GENERATING',
QUEUED: 'QUEUED',
READY: 'READY',
} as const;
export type DownloadStatusType = keyof typeof DownloadStatuses;

View File

@ -9,8 +9,8 @@
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { DownloadStatus } from './downloadStatus';
import { IDownloadStatus } from './downloadStatus';
export interface DownloadStatusResponse {
downloadStatus?: Array<DownloadStatus>;
downloadStatus?: IDownloadStatus[];
}

View File

@ -10,10 +10,12 @@
* Do not edit the class manually.
*/
import { List } from '../red-types';
/**
* Object containing information on which file and report types should be included in the download.
*/
export interface PrepareDownloadRequest {
dossierId?: string;
fileIds?: Array<string>;
readonly dossierId?: string;
readonly fileIds?: List;
}