RED-10422 - improved number of requests with help of new changes endpoint
This commit is contained in:
parent
8871e24660
commit
9484ce2377
@ -41,7 +41,7 @@ import { Roles } from '@users/roles';
|
|||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
||||||
import { merge, Observable } from 'rxjs';
|
import { merge, Observable } from 'rxjs';
|
||||||
import { filter, skip, switchMap, tap } from 'rxjs/operators';
|
import { filter, map, skip, switchMap, tap } from 'rxjs/operators';
|
||||||
import { ConfigService } from '../config.service';
|
import { ConfigService } from '../config.service';
|
||||||
import { BulkActionsService } from '../services/bulk-actions.service';
|
import { BulkActionsService } from '../services/bulk-actions.service';
|
||||||
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
||||||
@ -145,8 +145,9 @@ export default class DossierOverviewScreenComponent extends ListingComponent<Fil
|
|||||||
|
|
||||||
get #dossierFilesChange$() {
|
get #dossierFilesChange$() {
|
||||||
return this._dossiersService.dossierFileChanges$.pipe(
|
return this._dossiersService.dossierFileChanges$.pipe(
|
||||||
filter(dossierId => dossierId === this.dossierId && !!this._dossiersCacheService.get(dossierId)),
|
map(changes => changes[this.dossierId]),
|
||||||
switchMap(dossierId => this._filesService.loadAll(dossierId)),
|
filter(changes => !!changes && !!this._dossiersCacheService.get(this.dossierId)),
|
||||||
|
switchMap(changes => this._filesService.loadByIds({ [this.dossierId]: changes }).pipe(map(files => files[this.dossierId]))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -128,7 +128,8 @@ export class FilePreviewStateService {
|
|||||||
|
|
||||||
get #dossierFilesChange$() {
|
get #dossierFilesChange$() {
|
||||||
return this._dossiersService.dossierFileChanges$.pipe(
|
return this._dossiersService.dossierFileChanges$.pipe(
|
||||||
filter(dossierId => dossierId === this.dossierId),
|
map(changes => changes[this.dossierId]),
|
||||||
|
filter(fileIds => fileIds && fileIds.length > 0),
|
||||||
map(() => true),
|
map(() => true),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
import { GenericService, QueryParam, ROOT_CHANGES_KEY } from '@iqser/common-ui';
|
import { GenericService, ROOT_CHANGES_KEY } from '@iqser/common-ui';
|
||||||
import { Dossier, DossierStats, IDossierChanges } from '@red/domain';
|
import { Dossier, DossierStats, IChangesDetails } from '@red/domain';
|
||||||
import { forkJoin, Observable, of, Subscription, throwError, timer } from 'rxjs';
|
import { forkJoin, Observable, Subscription, timer } from 'rxjs';
|
||||||
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { ActiveDossiersService } from './active-dossiers.service';
|
import { ActiveDossiersService } from './active-dossiers.service';
|
||||||
import { ArchivedDossiersService } from './archived-dossiers.service';
|
import { ArchivedDossiersService } from './archived-dossiers.service';
|
||||||
import { inject, Injectable, OnDestroy } from '@angular/core';
|
import { inject, Injectable, OnDestroy } from '@angular/core';
|
||||||
import { DashboardStatsService } from '../dossier-templates/dashboard-stats.service';
|
import { DashboardStatsService } from '../dossier-templates/dashboard-stats.service';
|
||||||
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
|
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { filterEventsOnPages } from '@utils/operators';
|
import { filterEventsOnPages } from '@utils/operators';
|
||||||
|
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DossiersChangesService extends GenericService<Dossier> implements OnDestroy {
|
export class DossiersChangesService extends GenericService<Dossier> implements OnDestroy {
|
||||||
@ -19,40 +18,39 @@ export class DossiersChangesService extends GenericService<Dossier> implements O
|
|||||||
readonly #activeDossiersService = inject(ActiveDossiersService);
|
readonly #activeDossiersService = inject(ActiveDossiersService);
|
||||||
readonly #archivedDossiersService = inject(ArchivedDossiersService);
|
readonly #archivedDossiersService = inject(ArchivedDossiersService);
|
||||||
readonly #dashboardStatsService = inject(DashboardStatsService);
|
readonly #dashboardStatsService = inject(DashboardStatsService);
|
||||||
|
readonly #dossierStatsService = inject(DossierStatsService);
|
||||||
readonly #logger = inject(NGXLogger);
|
readonly #logger = inject(NGXLogger);
|
||||||
readonly #router = inject(Router);
|
readonly #router = inject(Router);
|
||||||
protected readonly _defaultModelPath = 'dossier';
|
protected readonly _defaultModelPath = 'dossier';
|
||||||
|
|
||||||
loadOnlyChanged(): Observable<IDossierChanges> {
|
loadOnlyChanged(): Observable<IChangesDetails> {
|
||||||
const removeIfNotFound = (id: string) =>
|
const load = (changes: IChangesDetails) => this.#load(changes.dossierChanges.map(d => d.dossierId));
|
||||||
catchError((error: unknown) => {
|
|
||||||
if (error instanceof HttpErrorResponse && error.status === HttpStatusCode.NotFound) {
|
|
||||||
this.#activeDossiersService.remove(id);
|
|
||||||
this.#archivedDossiersService.remove(id);
|
|
||||||
return of([]);
|
|
||||||
}
|
|
||||||
return throwError(() => error);
|
|
||||||
});
|
|
||||||
|
|
||||||
const load = (changes: IDossierChanges) =>
|
const loadStats = (change: IChangesDetails) => {
|
||||||
changes.map(change => this.#load(change.dossierId).pipe(removeIfNotFound(change.dossierId)));
|
const dossierStatsToLoad = new Set<string>();
|
||||||
|
change.dossierChanges.forEach(dossierChange => dossierStatsToLoad.add(dossierChange.dossierId));
|
||||||
|
change.fileChanges.forEach(fileChange => dossierStatsToLoad.add(fileChange.dossierId));
|
||||||
|
return this.#dossierStatsService.getFor(Array.from(dossierStatsToLoad));
|
||||||
|
};
|
||||||
|
|
||||||
return this.hasChangesDetails$().pipe(
|
return this.hasChangesDetails$().pipe(
|
||||||
tap(changes => this.#logger.info('[DOSSIERS_CHANGES] Found changes', changes)),
|
tap(changes => this.#logger.info('[DOSSIERS_CHANGES] Found changes', changes)),
|
||||||
switchMap(dossierChanges =>
|
switchMap(dossierChanges =>
|
||||||
forkJoin([...load(dossierChanges), this.#dashboardStatsService.loadAll().pipe(take(1))]).pipe(map(() => dossierChanges)),
|
forkJoin([load(dossierChanges), loadStats(dossierChanges), this.#dashboardStatsService.loadAll().pipe(take(1))]).pipe(
|
||||||
|
map(() => dossierChanges),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasChangesDetails$(): Observable<IDossierChanges> {
|
hasChangesDetails$(): Observable<IChangesDetails> {
|
||||||
const body = { value: this._lastCheckedForChanges.get(ROOT_CHANGES_KEY) };
|
const body = { value: this._lastCheckedForChanges.get(ROOT_CHANGES_KEY) };
|
||||||
const dateBeforeRequest = new Date().toISOString();
|
const dateBeforeRequest = new Date().toISOString();
|
||||||
|
|
||||||
this.#logger.info('[DOSSIERS_CHANGES] Check with Last Checked Date', body.value);
|
this.#logger.info('[DOSSIERS_CHANGES] Check with Last Checked Date', body.value);
|
||||||
|
|
||||||
return this._post<IDossierChanges>(body, `${this._defaultModelPath}/changes/details`).pipe(
|
return this._post<IChangesDetails>(body, `${this._defaultModelPath}/changes/details/v2`).pipe(
|
||||||
filter(changes => changes.length > 0),
|
filter(changes => changes.dossierChanges.length > 0 || changes.fileChanges.length > 0),
|
||||||
tap(() => this._lastCheckedForChanges.set(ROOT_CHANGES_KEY, dateBeforeRequest)),
|
tap(() => this._lastCheckedForChanges.set(ROOT_CHANGES_KEY, dateBeforeRequest)),
|
||||||
tap(() => this.#logger.info('[DOSSIERS_CHANGES] Save Last Checked Date value', dateBeforeRequest)),
|
tap(() => this.#logger.info('[DOSSIERS_CHANGES] Save Last Checked Date value', dateBeforeRequest)),
|
||||||
);
|
);
|
||||||
@ -75,17 +73,27 @@ export class DossiersChangesService extends GenericService<Dossier> implements O
|
|||||||
this.#subscription.unsubscribe();
|
this.#subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
#load(id: string): Observable<DossierStats[]> {
|
getByIds(ids: string[]) {
|
||||||
const queryParams: List<QueryParam> = [{ key: 'includeArchived', value: true }];
|
return super._post<Record<string, Dossier>>({ value: ids }, `${this._defaultModelPath}/by-id`);
|
||||||
return super._getOne([id], this._defaultModelPath, queryParams).pipe(
|
}
|
||||||
map(entity => new Dossier(entity)),
|
|
||||||
switchMap((dossier: Dossier) => {
|
#load(ids: string[]): Observable<Dossier[]> {
|
||||||
if (dossier.isArchived) {
|
return this.getByIds(ids).pipe(
|
||||||
this.#activeDossiersService.remove(dossier.id);
|
map(entity => {
|
||||||
return this.#archivedDossiersService.updateDossier(dossier);
|
return Object.values(entity).map(dossier => new Dossier(dossier));
|
||||||
}
|
}),
|
||||||
this.#archivedDossiersService.remove(dossier.id);
|
map((dossiers: Dossier[]) => {
|
||||||
return this.#activeDossiersService.updateDossier(dossier);
|
const archivedDossiers = dossiers.filter(dossier => dossier.isArchived);
|
||||||
|
const deletedDossiers = dossiers.filter(dossier => dossier.isSoftDeleted);
|
||||||
|
const activeDossiers = dossiers.filter(dossier => !dossier.isArchived && !dossier.isSoftDeleted);
|
||||||
|
|
||||||
|
archivedDossiers.forEach(dossier => this.#activeDossiersService.remove(dossier.id));
|
||||||
|
activeDossiers.forEach(dossier => this.#archivedDossiersService.remove(dossier.id));
|
||||||
|
deletedDossiers.forEach(dossier => this.#activeDossiersService.remove(dossier.id));
|
||||||
|
|
||||||
|
this.#activeDossiersService.updateDossiers(activeDossiers);
|
||||||
|
this.#archivedDossiersService.updateDossiers(archivedDossiers);
|
||||||
|
return dossiers;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { EntitiesService, Toaster } from '@iqser/common-ui';
|
import { EntitiesService, Toaster } from '@iqser/common-ui';
|
||||||
import { Dossier, DossierStats, IDossier, IDossierChanges, IDossierRequest } from '@red/domain';
|
import { Dossier, DossierFileChanges, DossierStats, IChangesDetails, IDossier, IDossierRequest } from '@red/domain';
|
||||||
import { Observable, of, Subject } from 'rxjs';
|
import { Observable, of, Subject } from 'rxjs';
|
||||||
import { catchError, map, switchMap, tap } from 'rxjs/operators';
|
import { catchError, map, switchMap, tap } from 'rxjs/operators';
|
||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
@ -17,7 +17,7 @@ export abstract class DossiersService extends EntitiesService<IDossier, Dossier>
|
|||||||
protected readonly _toaster = inject(Toaster);
|
protected readonly _toaster = inject(Toaster);
|
||||||
protected readonly _entityClass = Dossier;
|
protected readonly _entityClass = Dossier;
|
||||||
protected abstract readonly _defaultModelPath: string;
|
protected abstract readonly _defaultModelPath: string;
|
||||||
readonly dossierFileChanges$ = new Subject<string>();
|
readonly dossierFileChanges$ = new Subject<DossierFileChanges>();
|
||||||
abstract readonly routerPath: string;
|
abstract readonly routerPath: string;
|
||||||
|
|
||||||
createOrUpdate(dossier: IDossierRequest): Observable<Dossier> {
|
createOrUpdate(dossier: IDossierRequest): Observable<Dossier> {
|
||||||
@ -52,7 +52,18 @@ export abstract class DossiersService extends EntitiesService<IDossier, Dossier>
|
|||||||
return this._dossierStatsService.getFor([dossier.id]);
|
return this._dossierStatsService.getFor([dossier.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitFileChanges(dossierChanges: IDossierChanges): void {
|
updateDossiers(dossier: Dossier[]): void {
|
||||||
dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId));
|
dossier.forEach(d => this.replace(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
emitFileChanges(changes: IChangesDetails): void {
|
||||||
|
const changeModel: DossierFileChanges = {};
|
||||||
|
changes.fileChanges.forEach(change => {
|
||||||
|
if (!changeModel[change.dossierId]) {
|
||||||
|
changeModel[change.dossierId] = [];
|
||||||
|
}
|
||||||
|
changeModel[change.dossierId].push(change.fileId);
|
||||||
|
});
|
||||||
|
this.dossierFileChanges$.next(changeModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { EntitiesService, isArray, QueryParam } from '@iqser/common-ui';
|
import { EntitiesService, isArray, QueryParam } from '@iqser/common-ui';
|
||||||
import { List, mapEach } from '@iqser/common-ui/lib/utils';
|
import { List, mapEach } from '@iqser/common-ui/lib/utils';
|
||||||
import { ApproveResponse, File, IFile } from '@red/domain';
|
import { ApproveResponse, Dossier, DossierFileChanges, File, IFile } from '@red/domain';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
@ -27,8 +27,35 @@ export class FilesService extends EntitiesService<IFile, File> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadByIds(dossierFileChanges: DossierFileChanges) {
|
||||||
|
const filesByDossier$ = super
|
||||||
|
._post<{ value: Record<string, IFile[]> }>({ value: dossierFileChanges }, `${this._defaultModelPath}/by-id`)
|
||||||
|
.pipe(
|
||||||
|
map(response => {
|
||||||
|
const filesByDossier = response.value;
|
||||||
|
const result: Record<string, File[]> = {};
|
||||||
|
for (const key of Object.keys(filesByDossier)) {
|
||||||
|
result[key] = filesByDossier[key].map(file => new File(file, this._userService.getName(file.assignee)));
|
||||||
|
result[key].forEach(file => this._logger.info('[FILE] Loaded', file));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return filesByDossier$.pipe(
|
||||||
|
tap(files => {
|
||||||
|
for (const key of Object.keys(files)) {
|
||||||
|
const notDeletedFiles = files[key].filter(file => !file.deleted);
|
||||||
|
const deletedFiles = files[key].filter(file => file.deleted);
|
||||||
|
this._filesMapService.replace(key, notDeletedFiles);
|
||||||
|
deletedFiles.map(file => file.id).forEach(id => this._filesMapService.delete(key, id));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Reload dossier files + stats. */
|
/** Reload dossier files + stats. */
|
||||||
loadAll(dossierId: string) {
|
loadAll(dossierId: string) {
|
||||||
|
console.log('loadAll');
|
||||||
const files$ = this.getFor(dossierId).pipe(
|
const files$ = this.getFor(dossierId).pipe(
|
||||||
mapEach(file => new File(file, this._userService.getName(file.assignee))),
|
mapEach(file => new File(file, this._userService.getName(file.assignee))),
|
||||||
tap(file => this._logger.info('[FILE] Loaded', file)),
|
tap(file => this._logger.info('[FILE] Loaded', file)),
|
||||||
|
|||||||
@ -1,11 +1,19 @@
|
|||||||
export interface IDossierChange {
|
export interface IDossierChange {
|
||||||
readonly dossierChanges: boolean;
|
readonly dossierChanges: boolean;
|
||||||
readonly dossierId: string;
|
readonly dossierId: string;
|
||||||
readonly fileChanges: boolean;
|
readonly lastUpdated: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFileChange extends IDossierChange {
|
||||||
|
readonly fileId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IDossierChanges = readonly IDossierChange[];
|
export type IDossierChanges = readonly IDossierChange[];
|
||||||
|
export type IFileChanges = readonly IFileChange[];
|
||||||
|
|
||||||
export interface IChangesDetails {
|
export interface IChangesDetails {
|
||||||
readonly dossierChanges: IDossierChanges;
|
readonly dossierChanges: IDossierChanges;
|
||||||
|
readonly fileChanges: IFileChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DossierFileChanges = Record<string, string[]>;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user