Revert "[ACS-9166] Migrate Saved Searches to preferences API from config file…" (#10566)

This reverts commit 184a38a0ef.
This commit is contained in:
MichalKinas
2025-01-20 12:38:27 +01:00
committed by GitHub
parent c4b3a53d56
commit fcd6e25dc6
5 changed files with 130 additions and 171 deletions

View File

@@ -17,9 +17,9 @@
import { TestBed } from '@angular/core/testing';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { AlfrescoApiServiceMock } from '../../mock';
import { NodeEntry } from '@alfresco/js-api';
import { SavedSearchesService } from './saved-searches.service';
import { AlfrescoApiServiceMock } from '@alfresco/adf-content-services';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { AuthenticationService } from '@alfresco/adf-core';
import { Subject } from 'rxjs';
@@ -28,9 +28,10 @@ describe('SavedSearchesService', () => {
let service: SavedSearchesService;
let authService: AuthenticationService;
let testUserName: string;
let getNodeContentSpy: jasmine.Spy;
const testNodeId = 'test-node-id';
const LOCAL_STORAGE_KEY = 'saved-searches-test-user-migrated';
const SAVED_SEARCHES_NODE_ID = 'saved-searches-node-id__';
const SAVED_SEARCHES_CONTENT = JSON.stringify([
{ name: 'Search 1', description: 'Description 1', encodedUrl: 'url1', order: 0 },
{ name: 'Search 2', description: 'Description 2', encodedUrl: 'url2', order: 1 }
@@ -58,28 +59,23 @@ describe('SavedSearchesService', () => {
service = TestBed.inject(SavedSearchesService);
authService = TestBed.inject(AuthenticationService);
spyOn(service.nodesApi, 'getNode').and.callFake(() => Promise.resolve({ entry: { id: testNodeId } } as NodeEntry));
spyOn(service.nodesApi, 'getNodeContent').and.callFake(() => createBlob());
spyOn(service.nodesApi, 'deleteNode').and.callFake(() => Promise.resolve());
spyOn(service.preferencesApi, 'getPreference').and.callFake(() =>
Promise.resolve({ entry: { id: 'saved-searches', value: SAVED_SEARCHES_CONTENT } })
);
spyOn(service.preferencesApi, 'updatePreference').and.callFake(() =>
Promise.resolve({ entry: { id: 'saved-searches', value: SAVED_SEARCHES_CONTENT } })
);
spyOn(service.nodesApi, 'createNode').and.callFake(() => Promise.resolve({ entry: { id: 'new-node-id' } }));
spyOn(service.nodesApi, 'updateNodeContent').and.callFake(() => Promise.resolve({ entry: {} } as NodeEntry));
getNodeContentSpy = spyOn(service.nodesApi, 'getNodeContent').and.callFake(() => createBlob());
});
afterEach(() => {
localStorage.removeItem(LOCAL_STORAGE_KEY);
localStorage.removeItem(SAVED_SEARCHES_NODE_ID + testUserName);
});
it('should retrieve saved searches from the preferences API', (done) => {
it('should retrieve saved searches from the config.json file', (done) => {
spyOn(authService, 'getUsername').and.callFake(() => testUserName);
spyOn(localStorage, 'getItem').and.callFake(() => 'true');
service.init();
spyOn(localStorage, 'getItem').and.callFake(() => testNodeId);
service.innit();
service.getSavedSearches().subscribe((searches) => {
expect(localStorage.getItem).toHaveBeenCalledWith(LOCAL_STORAGE_KEY);
expect(service.preferencesApi.getPreference).toHaveBeenCalledWith('-me-', 'saved-searches');
expect(localStorage.getItem).toHaveBeenCalledWith(SAVED_SEARCHES_NODE_ID + testUserName);
expect(getNodeContentSpy).toHaveBeenCalledWith(testNodeId);
expect(searches.length).toBe(2);
expect(searches[0].name).toBe('Search 1');
expect(searches[1].name).toBe('Search 2');
@@ -87,43 +83,48 @@ describe('SavedSearchesService', () => {
});
});
it('should automatically migrate saved searches if config.json file exists', (done) => {
spyOn(localStorage, 'setItem');
it('should create config.json file if it does not exist', (done) => {
const error: Error = { name: 'test', message: '{ "error": { "statusCode": 404 } }' };
spyOn(authService, 'getUsername').and.callFake(() => testUserName);
service.nodesApi.getNode = jasmine.createSpy().and.returnValue(Promise.reject(error));
getNodeContentSpy.and.callFake(() => Promise.resolve(new Blob([''])));
service.innit();
service.getSavedSearches().subscribe((searches) => {
expect(service.nodesApi.getNode).toHaveBeenCalledWith('-my-', { relativePath: 'config.json' });
expect(service.nodesApi.getNodeContent).toHaveBeenCalledWith(testNodeId);
expect(localStorage.setItem).toHaveBeenCalledWith(LOCAL_STORAGE_KEY, 'true');
expect(service.preferencesApi.updatePreference).toHaveBeenCalledWith('-me-', 'saved-searches', SAVED_SEARCHES_CONTENT);
expect(service.nodesApi.deleteNode).toHaveBeenCalledWith(testNodeId, { permanent: true });
expect(searches.length).toBe(2);
expect(service.nodesApi.createNode).toHaveBeenCalledWith('-my-', jasmine.objectContaining({ name: 'config.json' }));
expect(searches.length).toBe(0);
done();
});
});
it('should save a new search', (done) => {
spyOn(authService, 'getUsername').and.callFake(() => testUserName);
spyOn(localStorage, 'getItem').and.callFake(() => 'true');
const nodeId = 'saved-searches-node-id';
spyOn(localStorage, 'getItem').and.callFake(() => nodeId);
const newSearch = { name: 'Search 3', description: 'Description 3', encodedUrl: 'url3' };
service.init();
service.innit();
service.saveSearch(newSearch).subscribe(() => {
expect(service.preferencesApi.updatePreference).toHaveBeenCalledWith('-me-', 'saved-searches', jasmine.any(String));
expect(service.nodesApi.updateNodeContent).toHaveBeenCalledWith(nodeId, jasmine.any(String));
expect(service.savedSearches$).toBeDefined();
done();
service.savedSearches$.subscribe((searches) => {
expect(searches.length).toBe(3);
expect(searches[2].name).toBe('Search 2');
expect(searches[2].order).toBe(2);
done();
});
});
});
it('should emit initial saved searches on subscription', (done) => {
spyOn(authService, 'getUsername').and.callFake(() => testUserName);
spyOn(localStorage, 'getItem').and.returnValue('true');
service.init();
const nodeId = 'saved-searches-node-id';
spyOn(localStorage, 'getItem').and.returnValue(nodeId);
service.innit();
service.savedSearches$.pipe().subscribe((searches) => {
expect(searches.length).toBe(2);
expect(searches[0].name).toBe('Search 1');
expect(service.preferencesApi.getPreference).toHaveBeenCalledWith('-me-', 'saved-searches');
done();
});
@@ -132,18 +133,25 @@ describe('SavedSearchesService', () => {
it('should emit updated saved searches after saving a new search', (done) => {
spyOn(authService, 'getUsername').and.callFake(() => testUserName);
spyOn(localStorage, 'getItem').and.callFake(() => 'true');
spyOn(localStorage, 'getItem').and.callFake(() => testNodeId);
const newSearch = { name: 'Search 3', description: 'Description 3', encodedUrl: 'url3' };
service.init();
service.innit();
service.saveSearch(newSearch).subscribe(() => {
service.savedSearches$.subscribe((searches) => {
let emissionCount = 0;
service.savedSearches$.subscribe((searches) => {
emissionCount++;
if (emissionCount === 1) {
expect(searches.length).toBe(2);
}
if (emissionCount === 2) {
expect(searches.length).toBe(3);
expect(searches[2].name).toBe('Search 2');
expect(service.preferencesApi.updatePreference).toHaveBeenCalledWith('-me-', 'saved-searches', jasmine.any(String));
done();
});
}
});
service.saveSearch(newSearch).subscribe();
});
it('should edit a search', (done) => {
@@ -182,7 +190,8 @@ describe('SavedSearchesService', () => {
*/
function prepareDefaultMock(): void {
spyOn(authService, 'getUsername').and.callFake(() => testUserName);
spyOn(localStorage, 'getItem').and.callFake(() => 'true');
service.init();
const nodeId = 'saved-searches-node-id';
spyOn(localStorage, 'getItem').and.callFake(() => nodeId);
service.innit();
}
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { NodesApi, NodeEntry, PreferencesApi } from '@alfresco/js-api';
import { NodesApi, 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';
@@ -27,25 +27,21 @@ import { AuthenticationService } from '@alfresco/adf-core';
providedIn: 'root'
})
export class SavedSearchesService {
private savedSearchFileNodeId: string;
private _nodesApi: NodesApi;
private _preferencesApi: PreferencesApi;
get nodesApi(): NodesApi {
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
return this._nodesApi;
}
get preferencesApi(): PreferencesApi {
this._preferencesApi = this._preferencesApi ?? new PreferencesApi(this.apiService.getInstance());
return this._preferencesApi;
}
readonly savedSearches$ = new ReplaySubject<SavedSearch[]>(1);
private savedSearchFileNodeId: string;
private currentUserLocalStorageKey: string;
private createFileAttempt = false;
constructor(private readonly apiService: AlfrescoApiService, private readonly authService: AuthenticationService) {}
init(): void {
innit(): void {
this.fetchSavedSearches();
}
@@ -55,27 +51,20 @@ export class SavedSearchesService {
* @returns SavedSearch list containing user saved searches
*/
getSavedSearches(): Observable<SavedSearch[]> {
const savedSearchesMigrated = localStorage.getItem(this.getLocalStorageKey()) ?? '';
if (savedSearchesMigrated === 'true') {
return from(this.preferencesApi.getPreference('-me-', 'saved-searches')).pipe(
map((preference) => JSON.parse(preference.entry.value)),
catchError(() => of([]))
);
} else {
return this.getSavedSearchesNodeId().pipe(
take(1),
concatMap(() => {
if (this.savedSearchFileNodeId !== '') {
return this.migrateSavedSearches();
} else {
return from(this.preferencesApi.getPreference('-me-', 'saved-searches')).pipe(
map((preference) => JSON.parse(preference.entry.value)),
catchError(() => of([]))
);
}
})
);
}
return this.getSavedSearchesNodeId().pipe(
concatMap(() =>
from(this.nodesApi.getNodeContent(this.savedSearchFileNodeId).then((content) => this.mapFileContentToSavedSearches(content))).pipe(
catchError((error) => {
if (!this.createFileAttempt) {
this.createFileAttempt = true;
localStorage.removeItem(this.getLocalStorageKey());
return this.getSavedSearches();
}
return throwError(() => error);
})
)
)
);
}
/**
@@ -105,8 +94,7 @@ export class SavedSearchesService {
order: index
}));
return from(this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(updatedSavedSearches))).pipe(
map((preference) => JSON.parse(preference.entry.value)),
return from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSavedSearches))).pipe(
tap(() => this.savedSearches$.next(updatedSavedSearches))
);
}),
@@ -135,9 +123,7 @@ export class SavedSearchesService {
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))
)
from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSearches)))
),
catchError((error) => {
this.savedSearches$.next(previousSavedSearches);
@@ -168,9 +154,7 @@ export class SavedSearchesService {
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))
)
from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSearches)))
),
catchError((error) => {
this.savedSearches$.next(previousSavedSearchesOrder);
@@ -201,9 +185,7 @@ export class SavedSearchesService {
}),
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))
)
from(this.nodesApi.updateNodeContent(this.savedSearchFileNodeId, JSON.stringify(updatedSearches)))
),
catchError((error) => {
this.savedSearches$.next(previousSavedSearchesOrder);
@@ -214,33 +196,52 @@ export class SavedSearchesService {
}
private getSavedSearchesNodeId(): Observable<string> {
return from(this.nodesApi.getNode('-my-', { relativePath: 'config.json' })).pipe(
first(),
concatMap((configNode) => {
this.savedSearchFileNodeId = configNode.entry.id;
return configNode.entry.id;
}),
catchError((error) => {
const errorStatusCode = JSON.parse(error.message).error.statusCode;
if (errorStatusCode === 404) {
localStorage.setItem(this.getLocalStorageKey(), 'true');
return '';
} else {
return throwError(() => error);
}
})
);
const localStorageKey = this.getLocalStorageKey();
if (this.currentUserLocalStorageKey && this.currentUserLocalStorageKey !== localStorageKey) {
this.savedSearches$.next([]);
}
this.currentUserLocalStorageKey = localStorageKey;
let savedSearchesNodeId = localStorage.getItem(this.currentUserLocalStorageKey) ?? '';
if (savedSearchesNodeId === '') {
return from(this.nodesApi.getNode('-my-', { relativePath: 'config.json' })).pipe(
first(),
concatMap((configNode) => {
savedSearchesNodeId = configNode.entry.id;
localStorage.setItem(this.currentUserLocalStorageKey, savedSearchesNodeId);
this.savedSearchFileNodeId = savedSearchesNodeId;
return savedSearchesNodeId;
}),
catchError((error) => {
const errorStatusCode = JSON.parse(error.message).error.statusCode;
if (errorStatusCode === 404) {
return this.createSavedSearchesNode('-my-').pipe(
first(),
map((node) => {
localStorage.setItem(this.currentUserLocalStorageKey, node.entry.id);
return node.entry.id;
})
);
} else {
return throwError(() => error);
}
})
);
} else {
this.savedSearchFileNodeId = savedSearchesNodeId;
return of(savedSearchesNodeId);
}
}
private createSavedSearchesNode(parentNodeId: string): Observable<NodeEntry> {
return from(this.nodesApi.createNode(parentNodeId, { name: 'config.json', nodeType: 'cm:content' }));
}
private async mapFileContentToSavedSearches(blob: Blob): Promise<Array<SavedSearch>> {
return blob
.text()
.then((content) => (content ? JSON.parse(content) : []))
.catch(() => []);
return blob.text().then((content) => (content ? JSON.parse(content) : []));
}
private getLocalStorageKey(): string {
return `saved-searches-${this.authService.getUsername()}-migrated`;
return `saved-searches-node-id__${this.authService.getUsername()}`;
}
private fetchSavedSearches(): void {
@@ -248,14 +249,4 @@ export class SavedSearchesService {
.pipe(take(1))
.subscribe((searches) => this.savedSearches$.next(searches));
}
private migrateSavedSearches(): Observable<SavedSearch[]> {
return from(this.nodesApi.getNodeContent(this.savedSearchFileNodeId).then((content) => this.mapFileContentToSavedSearches(content))).pipe(
tap((savedSearches) => {
this.preferencesApi.updatePreference('-me-', 'saved-searches', JSON.stringify(savedSearches));
localStorage.setItem(this.getLocalStorageKey(), 'true');
this.nodesApi.deleteNode(this.savedSearchFileNodeId, { permanent: true });
})
);
}
}