From af66fecfae467891f09efb80b34629ee2118c78e Mon Sep 17 00:00:00 2001 From: Anton Ramanovich Date: Fri, 24 Oct 2025 23:00:05 +0200 Subject: [PATCH] [ACS-9166]: migrates both saved searches to inheritance model --- .../services/saved-searches-legacy.service.ts | 143 ++--------------- .../common/services/saved-searches.service.ts | 146 ++---------------- 2 files changed, 23 insertions(+), 266 deletions(-) diff --git a/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts b/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts index e558606218..8fb9196b99 100644 --- a/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts +++ b/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts @@ -15,42 +15,28 @@ * limitations under the License. */ -import { NodesApi, NodeEntry } from '@alfresco/js-api'; +import { NodeEntry } from '@alfresco/js-api'; import { Injectable } from '@angular/core'; -import { Observable, of, from, ReplaySubject, throwError } from 'rxjs'; -import { catchError, concatMap, first, map, switchMap, take, tap } from 'rxjs/operators'; +import { Observable, of, from, throwError } from 'rxjs'; +import { catchError, concatMap, first, map } from 'rxjs/operators'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { SavedSearch } from '../interfaces/saved-search.interface'; import { AuthenticationService } from '@alfresco/adf-core'; -import { SavedSearchStrategy } from '../interfaces/saved-searches-strategy.interface'; +import { SavedSearchesBaseService } from './saved-searches-base.service'; @Injectable({ providedIn: 'root' }) -export class SavedSearchesLegacyService implements SavedSearchStrategy { - private _nodesApi: NodesApi; - get nodesApi(): NodesApi { - this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance()); - return this._nodesApi; - } - - private readonly _savedSearches$ = new ReplaySubject(1); - readonly savedSearches$ = this._savedSearches$.asObservable(); - +export class SavedSearchesLegacyService extends SavedSearchesBaseService { private savedSearchFileNodeId: string; private currentUserLocalStorageKey: string; private createFileAttempt = false; - constructor( - private readonly apiService: AlfrescoApiService, - private readonly authService: AuthenticationService - ) {} - - init(): void { - this.fetchSavedSearches(); + constructor(apiService: AlfrescoApiService, authService: AuthenticationService) { + super(apiService, authService); } - getSavedSearches(): Observable { + protected fetchAllSavedSearches(): Observable { return this.getSavedSearchesNodeId().pipe( concatMap(() => from(this.nodesApi.getNodeContent(this.savedSearchFileNodeId).then((content) => this.mapFileContentToSavedSearches(content))).pipe( @@ -58,7 +44,7 @@ export class SavedSearchesLegacyService implements SavedSearchStrategy { if (!this.createFileAttempt) { this.createFileAttempt = true; localStorage.removeItem(this.getLocalStorageKey()); - return this.getSavedSearches(); + return this.fetchAllSavedSearches(); } return throwError(() => error); }) @@ -67,113 +53,14 @@ export class SavedSearchesLegacyService implements SavedSearchStrategy { ); } - saveSearch(newSaveSearch: Pick): Observable { - return this.getSavedSearches().pipe( - take(1), - switchMap((savedSearches: SavedSearch[]) => { - let updatedSavedSearches: SavedSearch[] = []; - - if (savedSearches.length < 5) { - updatedSavedSearches = [{ ...newSaveSearch, order: 0 }, ...savedSearches]; - } else { - const firstFiveSearches = savedSearches.slice(0, 5); - const restOfSearches = savedSearches.slice(5); - - updatedSavedSearches = [...firstFiveSearches, { ...newSaveSearch, order: 5 }, ...restOfSearches]; - } - - updatedSavedSearches = updatedSavedSearches.map((search, index) => ({ - ...search, - order: index - })); - - return from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSavedSearches))).pipe( - tap(() => this._savedSearches$.next(updatedSavedSearches)) - ); - }), - catchError((error) => { - console.error('Error saving new search:', error); - return throwError(() => error); - }) - ); - } - - editSavedSearch(updatedSavedSearch: SavedSearch): Observable { - let previousSavedSearches: SavedSearch[]; - return this.savedSearches$.pipe( - take(1), - map((savedSearches: SavedSearch[]) => { - previousSavedSearches = [...savedSearches]; - return savedSearches.map((search) => (search.order === updatedSavedSearch.order ? updatedSavedSearch : search)); - }), - tap((updatedSearches: SavedSearch[]) => { - this._savedSearches$.next(updatedSearches); - }), - switchMap((updatedSearches: SavedSearch[]) => - from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSearches))) - ), - catchError((error) => { - this._savedSearches$.next(previousSavedSearches); - return throwError(() => error); - }) - ); - } - - deleteSavedSearch(deletedSavedSearch: SavedSearch): Observable { - let previousSavedSearchesOrder: SavedSearch[]; - return this._savedSearches$.pipe( - take(1), - map((savedSearches: SavedSearch[]) => { - previousSavedSearchesOrder = [...savedSearches]; - const updatedSearches = savedSearches.filter((search) => search.order !== deletedSavedSearch.order); - return updatedSearches.map((search, index) => ({ - ...search, - order: index - })); - }), - tap((updatedSearches: SavedSearch[]) => { - this._savedSearches$.next(updatedSearches); - }), - switchMap((updatedSearches: SavedSearch[]) => - from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSearches))) - ), - catchError((error) => { - this._savedSearches$.next(previousSavedSearchesOrder); - return throwError(() => error); - }) - ); - } - - changeOrder(previousIndex: number, currentIndex: number): void { - let previousSavedSearchesOrder: SavedSearch[]; - this.savedSearches$ - .pipe( - take(1), - map((savedSearches: SavedSearch[]) => { - previousSavedSearchesOrder = [...savedSearches]; - const [movedSearch] = savedSearches.splice(previousIndex, 1); - savedSearches.splice(currentIndex, 0, movedSearch); - return savedSearches.map((search, index) => ({ - ...search, - order: index - })); - }), - tap((savedSearches: SavedSearch[]) => this._savedSearches$.next(savedSearches)), - switchMap((updatedSearches: SavedSearch[]) => - from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSearches))) - ), - catchError((error) => { - this._savedSearches$.next(previousSavedSearchesOrder); - return throwError(() => error); - }) - ) - .subscribe(); + protected updateSavedSearches(searches: SavedSearch[]): Observable { + return from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(searches))); } private getSavedSearchesNodeId(): Observable { const localStorageKey = this.getLocalStorageKey(); if (this.currentUserLocalStorageKey && this.currentUserLocalStorageKey !== localStorageKey) { - this._savedSearches$.next([]); + this.resetSavedSearchesStream(); } this.currentUserLocalStorageKey = localStorageKey; let savedSearchesNodeId = localStorage.getItem(this.currentUserLocalStorageKey) ?? ''; @@ -218,10 +105,4 @@ export class SavedSearchesLegacyService implements SavedSearchStrategy { private getLocalStorageKey(): string { return `saved-searches-node-id__${this.authService.getUsername()}`; } - - private fetchSavedSearches(): void { - this.getSavedSearches() - .pipe(take(1)) - .subscribe((searches) => this._savedSearches$.next(searches)); - } } diff --git a/lib/content-services/src/lib/common/services/saved-searches.service.ts b/lib/content-services/src/lib/common/services/saved-searches.service.ts index 02ef7409e2..32fc945e6e 100644 --- a/lib/content-services/src/lib/common/services/saved-searches.service.ts +++ b/lib/content-services/src/lib/common/services/saved-searches.service.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { NodesApi, NodeEntry, PreferencesApi, ContentFieldsQuery, PreferenceEntry } from '@alfresco/js-api'; +import { NodeEntry, PreferencesApi, ContentFieldsQuery, PreferenceEntry } from '@alfresco/js-api'; import { inject, Injectable, InjectionToken } from '@angular/core'; -import { Observable, of, from, ReplaySubject, throwError } from 'rxjs'; +import { Observable, of, from, throwError } from 'rxjs'; import { catchError, concatMap, first, map, switchMap, take, tap } from 'rxjs/operators'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { SavedSearch } from '../interfaces/saved-search.interface'; import { AuthenticationService } from '@alfresco/adf-core'; -import { SavedSearchStrategy } from '../interfaces/saved-searches-strategy.interface'; +import { SavedSearchesBaseService } from './saved-searches-base.service'; export interface SavedSearchesPreferencesApiService { getPreference: (personId: string, preferenceName: string, opts?: ContentFieldsQuery) => Promise | Observable; @@ -34,17 +34,11 @@ export const SAVED_SEARCHES_SERVICE_PREFERENCES = new InjectionToken(1); - readonly savedSearches$ = this._savedSearches$.asObservable(); - - constructor( - private readonly apiService: AlfrescoApiService, - private readonly authService: AuthenticationService - ) {} - - init(): void { - this.fetchSavedSearches(); + constructor(apiService: AlfrescoApiService, authService: AuthenticationService) { + super(apiService, authService); } - getSavedSearches(): Observable { + protected fetchAllSavedSearches(): Observable { const savedSearchesMigrated = localStorage.getItem(this.getLocalStorageKey()) ?? ''; if (savedSearchesMigrated === 'true') { return this.getSavedSearchesFromPreferenceApi(); } else { return this.getSavedSearchesNodeId().pipe( take(1), - concatMap(() => { + switchMap(() => { if (this.savedSearchFileNodeId !== '') { return this.migrateSavedSearches(); } else { @@ -86,116 +72,12 @@ export class SavedSearchesService implements SavedSearchStrategy { } } - saveSearch(newSaveSearch: Pick): Observable { - return this.getSavedSearches().pipe( - take(1), - switchMap((savedSearches: SavedSearch[]) => { - let updatedSavedSearches: SavedSearch[] = []; - - if (savedSearches.length < 5) { - updatedSavedSearches = [{ ...newSaveSearch, order: 0 }, ...savedSearches]; - } else { - const firstFiveSearches = savedSearches.slice(0, 5); - const restOfSearches = savedSearches.slice(5); - - updatedSavedSearches = [...firstFiveSearches, { ...newSaveSearch, order: 5 }, ...restOfSearches]; - } - - updatedSavedSearches = updatedSavedSearches.map((search, index) => ({ - ...search, - order: index - })); - - return from(this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(updatedSavedSearches))).pipe( - map((preference) => JSON.parse(preference.entry.value)), - tap(() => this._savedSearches$.next(updatedSavedSearches)) - ); - }), - catchError((error) => { - console.error('Error saving new search:', error); - return throwError(() => error); - }) + protected updateSavedSearches(updatedSavedSearches: SavedSearch[]): Observable { + return from(this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(updatedSavedSearches))).pipe( + map((preference) => JSON.parse(preference.entry.value)) ); } - editSavedSearch(updatedSavedSearch: SavedSearch): Observable { - let previousSavedSearches: SavedSearch[]; - return this.savedSearches$.pipe( - take(1), - map((savedSearches: SavedSearch[]) => { - previousSavedSearches = [...savedSearches]; - return savedSearches.map((search) => (search.order === updatedSavedSearch.order ? updatedSavedSearch : search)); - }), - tap((updatedSearches: SavedSearch[]) => { - this._savedSearches$.next(updatedSearches); - }), - switchMap((updatedSearches: SavedSearch[]) => - from(this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(updatedSearches))).pipe( - map((preference) => JSON.parse(preference.entry.value)) - ) - ), - catchError((error) => { - this._savedSearches$.next(previousSavedSearches); - return throwError(() => error); - }) - ); - } - - deleteSavedSearch(deletedSavedSearch: SavedSearch): Observable { - let previousSavedSearchesOrder: SavedSearch[]; - return this.savedSearches$.pipe( - take(1), - map((savedSearches: SavedSearch[]) => { - previousSavedSearchesOrder = [...savedSearches]; - const updatedSearches = savedSearches.filter((search) => search.order !== deletedSavedSearch.order); - return updatedSearches.map((search, index) => ({ - ...search, - order: index - })); - }), - tap((updatedSearches: SavedSearch[]) => { - this._savedSearches$.next(updatedSearches); - }), - switchMap((updatedSearches: SavedSearch[]) => - from(this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(updatedSearches))).pipe( - map((preference) => JSON.parse(preference.entry.value)) - ) - ), - catchError((error) => { - this._savedSearches$.next(previousSavedSearchesOrder); - return throwError(() => error); - }) - ); - } - - changeOrder(previousIndex: number, currentIndex: number): void { - let previousSavedSearchesOrder: SavedSearch[]; - this.savedSearches$ - .pipe( - take(1), - map((savedSearches: SavedSearch[]) => { - previousSavedSearchesOrder = [...savedSearches]; - const [movedSearch] = savedSearches.splice(previousIndex, 1); - savedSearches.splice(currentIndex, 0, movedSearch); - return savedSearches.map((search, index) => ({ - ...search, - order: index - })); - }), - tap((savedSearches: SavedSearch[]) => this._savedSearches$.next(savedSearches)), - switchMap((updatedSearches: SavedSearch[]) => - from(this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(updatedSearches))).pipe( - map((preference) => JSON.parse(preference.entry.value)) - ) - ), - catchError((error) => { - this._savedSearches$.next(previousSavedSearchesOrder); - return throwError(() => error); - }) - ) - .subscribe(); - } - private getSavedSearchesNodeId(): Observable { return from(this.nodesApi.getNode('-my-', { relativePath: 'config.json' })).pipe( first(), @@ -224,12 +106,6 @@ export class SavedSearchesService implements SavedSearchStrategy { return `saved-searches-${this.authService.getUsername()}-migrated`; } - private fetchSavedSearches(): void { - this.getSavedSearches() - .pipe(take(1)) - .subscribe((searches) => this._savedSearches$.next(searches)); - } - private migrateSavedSearches(): Observable { return from(this.nodesApi.getNodeContent(this.savedSearchFileNodeId).then((content) => this.mapFileContentToSavedSearches(content))).pipe( tap((savedSearches) => {