mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
[ADF-4457] StorageService should be independent of AppConfigService (#4712)
* [ADF-4457] StorageService should be independent of AppConfigService * [ADF-4457] Fix e2e tests * [ADF-4457] Fix e2e tests * [ADF-4457] Improve storage service workflow * Fix linting * Fix unit tests * Fix e2e test * Add missing class to constructor * Fix e2e test * Rebase branch * Improve unit test * fix test
This commit is contained in:
committed by
Eugenio Romano
parent
90c403ae9e
commit
5c07d5b3e6
@@ -39,6 +39,7 @@ export enum AppConfigValues {
|
||||
DISABLECSRF = 'disableCSRF',
|
||||
AUTH_WITH_CREDENTIALS = 'auth.withCredentials',
|
||||
APPLICATION = 'application',
|
||||
STORAGE_PREFIX = 'application.storagePrefix',
|
||||
NOTIFY_DURATION = 'notificationDefaultDuration'
|
||||
}
|
||||
|
||||
|
@@ -17,26 +17,23 @@
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||
|
||||
@Injectable()
|
||||
export class DebugAppConfigService extends AppConfigService {
|
||||
constructor(http: HttpClient) {
|
||||
constructor(private storage: StorageService, http: HttpClient) {
|
||||
super(http);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
get<T>(key: string, defaultValue?: T): T {
|
||||
if (key === AppConfigValues.OAUTHCONFIG) {
|
||||
return <T> (JSON.parse(this.getItem(key)) || super.get<T>(key, defaultValue));
|
||||
return <T> (JSON.parse(this.storage.getItem(key)) || super.get<T>(key, defaultValue));
|
||||
} else if (key === AppConfigValues.APPLICATION) {
|
||||
return undefined;
|
||||
} else {
|
||||
return <T> (<any> this.getItem(key) || super.get<T>(key, defaultValue));
|
||||
return <T> (<any> this.storage.getItem(key) || super.get<T>(key, defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
getItem(key: string): string | null {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
@@ -17,14 +17,14 @@
|
||||
|
||||
import { DateCellComponent } from './date-cell.component';
|
||||
import { Subject } from 'rxjs';
|
||||
import { AlfrescoApiServiceMock, AppConfigService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
|
||||
describe('DataTableCellComponent', () => {
|
||||
let alfrescoApiService: AlfrescoApiServiceMock;
|
||||
|
||||
beforeEach(() => {
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null));
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
});
|
||||
|
||||
it('should use medium format by default', () => {
|
||||
|
@@ -102,7 +102,7 @@ describe('DownloadZipDialogComponent', () => {
|
||||
|
||||
it('should call cancelDownload when CANCEL button is clicked', () => {
|
||||
fixture.detectChanges();
|
||||
spyOn(component, 'cancelDownload').and.returnValue('');
|
||||
spyOn(component, 'cancelDownload');
|
||||
|
||||
const cancelButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#cancel-button');
|
||||
cancelButton.click();
|
||||
@@ -118,7 +118,7 @@ describe('DownloadZipDialogComponent', () => {
|
||||
|
||||
it('should close dialog when download is completed', () => {
|
||||
component.download('fakeUrl', 'fileName');
|
||||
spyOn(component, 'cancelDownload').and.returnValue('');
|
||||
spyOn(component, 'cancelDownload');
|
||||
fixture.detectChanges();
|
||||
expect(dialogRef.close).toHaveBeenCalled();
|
||||
});
|
||||
@@ -134,7 +134,7 @@ describe('DownloadZipDialogComponent', () => {
|
||||
it('should interrupt download when cancel button is clicked', () => {
|
||||
spyOn(component, 'downloadZip');
|
||||
spyOn(component, 'download');
|
||||
spyOn(component, 'cancelDownload').and.returnValue('');
|
||||
spyOn(component, 'cancelDownload');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
|
@@ -22,6 +22,7 @@ import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { setupTestBed } from '../testing/setupTestBed';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
|
||||
describe('NodeFavoriteDirective', () => {
|
||||
|
||||
@@ -33,7 +34,7 @@ describe('NodeFavoriteDirective', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null));
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
directive = new NodeFavoriteDirective( alfrescoApiService);
|
||||
});
|
||||
|
||||
|
@@ -18,15 +18,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
|
||||
/* tslint:disable:adf-file-name */
|
||||
@Injectable()
|
||||
export class AlfrescoApiServiceMock extends AlfrescoApiService {
|
||||
|
||||
constructor(protected appConfig: AppConfigService) {
|
||||
super(appConfig);
|
||||
constructor(protected appConfig: AppConfigService,
|
||||
protected storageService: StorageService) {
|
||||
super(appConfig, storageService);
|
||||
if (!this.alfrescoApi) {
|
||||
this.initAlfrescoApi();
|
||||
}
|
||||
}
|
||||
|
||||
initialize(): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
this.alfrescoApiInitializedSubject.next();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -25,8 +25,9 @@ import {
|
||||
} from '@alfresco/js-api';
|
||||
import { AlfrescoApiCompatibility, AlfrescoApiConfig } from '@alfresco/js-api';
|
||||
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
/* tslint:disable:adf-file-name */
|
||||
|
||||
@@ -39,6 +40,9 @@ export class AlfrescoApiService {
|
||||
*/
|
||||
nodeUpdated = new Subject<Node>();
|
||||
|
||||
protected alfrescoApiInitializedSubject: Subject<any>;
|
||||
alfrescoApiInitialized: Observable<any>;
|
||||
|
||||
protected alfrescoApi: AlfrescoApiCompatibility;
|
||||
|
||||
lastConfig: AlfrescoApiConfig;
|
||||
@@ -95,12 +99,18 @@ export class AlfrescoApiService {
|
||||
return this.getInstance().core.groupsApi;
|
||||
}
|
||||
|
||||
constructor(protected appConfig: AppConfigService) {
|
||||
constructor(
|
||||
protected appConfig: AppConfigService,
|
||||
protected storageService: StorageService) {
|
||||
this.alfrescoApiInitializedSubject = new Subject();
|
||||
this.alfrescoApiInitialized = this.alfrescoApiInitializedSubject.asObservable();
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.appConfig.load().then(() => {
|
||||
this.storageService.prefix = this.appConfig.get<string>(AppConfigValues.STORAGE_PREFIX, '');
|
||||
this.initAlfrescoApi();
|
||||
this.alfrescoApiInitializedSubject.next();
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -40,7 +40,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
});
|
||||
|
||||
it('Should canActivate be true if the Role is present int the JWT token', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role1'] } });
|
||||
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
@@ -50,7 +50,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
}));
|
||||
|
||||
it('Should canActivate be false if the Role is not present int the JWT token', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role3'] } });
|
||||
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
@@ -60,7 +60,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
}));
|
||||
|
||||
it('Should not redirect if canActivate is', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role1'] } });
|
||||
spyOn(routerService, 'navigate').and.stub();
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
}));
|
||||
|
||||
it('Should canActivate return false if the data Role to check is empty', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role1', 'role3'] } });
|
||||
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
@@ -81,7 +81,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
}));
|
||||
|
||||
it('Should canActivate return false if the realm_access is not present', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({});
|
||||
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
@@ -90,7 +90,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
}));
|
||||
|
||||
it('Should redirect to the redirectURL if canActivate is false and redirectUrl is in data', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({});
|
||||
spyOn(routerService, 'navigate').and.stub();
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
}));
|
||||
|
||||
it('Should not redirect if canActivate is false and redirectUrl is not in data', async(() => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({});
|
||||
spyOn(routerService, 'navigate').and.stub();
|
||||
|
||||
@@ -137,7 +137,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
|
||||
it('Should canActivate be true if both Real Role and Client Role are present int the JWT token', () => {
|
||||
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
|
||||
'realm_access': { roles: ['role1'] },
|
||||
@@ -152,7 +152,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
|
||||
it('Should canActivate be false if the Client Role is not present int the JWT token with the correct role', () => {
|
||||
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
|
||||
'realm_access': { roles: ['role1'] },
|
||||
@@ -168,7 +168,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
describe('ClientRole ', () => {
|
||||
|
||||
it('Should be true if the resource_access contains the single role', () => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{
|
||||
@@ -180,7 +180,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
});
|
||||
|
||||
it('Should be true if the resource_access contains at least one of the roles', () => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{
|
||||
@@ -192,7 +192,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
});
|
||||
|
||||
it('Should be false if the resource_access does not contain the role', () => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{
|
||||
'resource_access': { fakeapp: { roles: ['role3'] } }
|
||||
@@ -202,7 +202,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
});
|
||||
|
||||
it('Should be false if the resource_access does not contain the client role related to the app', () => {
|
||||
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{
|
||||
'resource_access': { anotherfakeapp: { roles: ['role1'] } }
|
||||
|
@@ -18,7 +18,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { JwtHelperService } from './jwt-helper.service';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -52,28 +51,22 @@ export class AuthGuardSsoRoleService implements CanActivate {
|
||||
return hasRole;
|
||||
}
|
||||
|
||||
constructor(private storageService: StorageService, private jwtHelperService: JwtHelperService, private router: Router) {
|
||||
constructor(private jwtHelperService: JwtHelperService, private router: Router) {
|
||||
}
|
||||
|
||||
getRealmRoles(): string[] {
|
||||
const access = this.jwtHelperService.getValueFromLocalAccessToken<any>('realm_access');
|
||||
const roles = access ? access['roles'] : [];
|
||||
return roles;
|
||||
return access ? access['roles'] : [];
|
||||
}
|
||||
|
||||
getClientRoles(client: string): string[] {
|
||||
const clientRole = this.jwtHelperService.getValueFromLocalAccessToken<any>('resource_access')[client];
|
||||
const roles = clientRole ? clientRole['roles'] : [];
|
||||
return roles;
|
||||
}
|
||||
|
||||
getAccessToken(): string {
|
||||
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
|
||||
return clientRole ? clientRole['roles'] : [];
|
||||
}
|
||||
|
||||
hasRealmRole(role: string): boolean {
|
||||
let hasRole = false;
|
||||
if (this.getAccessToken()) {
|
||||
if (this.jwtHelperService.getAccessToken()) {
|
||||
const realmRoles = this.getRealmRoles();
|
||||
hasRole = realmRoles.some((currentRole) => {
|
||||
return currentRole === role;
|
||||
@@ -96,7 +89,7 @@ export class AuthGuardSsoRoleService implements CanActivate {
|
||||
|
||||
hasClientRole(clientRole, role: string): boolean {
|
||||
let hasRole = false;
|
||||
if (this.getAccessToken()) {
|
||||
if (this.jwtHelperService.getAccessToken()) {
|
||||
const clientRoles = this.getClientRoles(clientRole);
|
||||
hasRole = clientRoles.some((currentRole) => {
|
||||
return currentRole === role;
|
||||
|
@@ -79,8 +79,15 @@ export class JwtHelperService {
|
||||
* @returns Value from the token
|
||||
*/
|
||||
getValueFromLocalAccessToken<T>(key: string): T {
|
||||
const accessToken = localStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
|
||||
return this.getValueFromToken(accessToken, key);
|
||||
return this.getValueFromToken(this.getAccessToken(), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets access token
|
||||
* @returns access token
|
||||
*/
|
||||
getAccessToken(): string {
|
||||
return localStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,69 +29,81 @@ describe('StorageService', () => {
|
||||
const key = 'test_key';
|
||||
const value = 'test_value';
|
||||
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [
|
||||
{ provide: AppConfigService, useClass: AppConfigServiceMock }
|
||||
]
|
||||
});
|
||||
describe('StorageService', () => {
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
appConfig = TestBed.get(AppConfigService);
|
||||
appConfig.config = {
|
||||
application: {
|
||||
storagePrefix: 'ADF_APP'
|
||||
}
|
||||
};
|
||||
storage = TestBed.get(StorageService);
|
||||
});
|
||||
beforeEach(() => {
|
||||
appConfig = TestBed.get(AppConfigService);
|
||||
appConfig.config = {
|
||||
application: {
|
||||
storagePrefix: 'ADF_APP'
|
||||
}
|
||||
};
|
||||
storage = TestBed.get(StorageService);
|
||||
});
|
||||
|
||||
it('should get the prefix for the storage from app config', (done) => {
|
||||
appConfig.load().then(() => {
|
||||
expect(storage.storagePrefix).toBe('ADF_APP_');
|
||||
done();
|
||||
it('should get the prefix for the storage from app config', (done) => {
|
||||
appConfig.load().then(() => {
|
||||
expect(storage.prefix).toBe('ADF_APP_');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set a property with the prefix in the local storage', (done) => {
|
||||
storage.clear();
|
||||
|
||||
appConfig.load().then(() => {
|
||||
storage.setItem(key, value);
|
||||
const storageKey = localStorage.key(0);
|
||||
expect(storageKey).toBe('ADF_APP_' + key);
|
||||
expect(localStorage.getItem(storageKey)).toBe(value);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to get a property from the local storage', (done) => {
|
||||
storage.clear();
|
||||
|
||||
appConfig.load().then(() => {
|
||||
storage.setItem(key, value);
|
||||
|
||||
expect(storage.getItem(key)).toBe(value);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should set an empty prefix when the it is not defined in the app config', (done) => {
|
||||
appConfig.config.application.storagePrefix = '';
|
||||
appConfig.load().then(() => {
|
||||
expect(storage.storagePrefix).toBe('');
|
||||
done();
|
||||
describe('StorageService', () => {
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule]
|
||||
});
|
||||
});
|
||||
|
||||
it('should set a property with the prefix in the local storage', (done) => {
|
||||
storage.clear();
|
||||
|
||||
appConfig.load().then(() => {
|
||||
storage.setItem(key, value);
|
||||
expect(localStorage.hasOwnProperty('ADF_APP_' + key)).not.toBe(null);
|
||||
expect(localStorage.getItem('ADF_APP_' + key)).toBe(value);
|
||||
done();
|
||||
beforeEach(() => {
|
||||
appConfig = TestBed.get(AppConfigService);
|
||||
appConfig.config = {
|
||||
application: {
|
||||
storagePrefix: ''
|
||||
}
|
||||
};
|
||||
storage = TestBed.get(StorageService);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set a property without a prefix in the local storage', (done) => {
|
||||
storage.clear();
|
||||
appConfig.config.application.storagePrefix = '';
|
||||
|
||||
appConfig.load().then(() => {
|
||||
storage.setItem(key, value);
|
||||
expect(localStorage.hasOwnProperty(key)).not.toBe(null);
|
||||
expect(localStorage.getItem(key)).toBe(value);
|
||||
done();
|
||||
it('should set an empty prefix when the it is not defined in the app config', (done) => {
|
||||
appConfig.load().then(() => {
|
||||
expect(storage.prefix).toBe('');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to get a property from the local storage', (done) => {
|
||||
storage.clear();
|
||||
it('should set a property without a prefix in the local storage', (done) => {
|
||||
appConfig.load().then(() => {
|
||||
storage.setItem(key, value);
|
||||
|
||||
appConfig.load().then(() => {
|
||||
storage.setItem(key, value);
|
||||
|
||||
expect(storage.getItem(key)).toBe(value);
|
||||
done();
|
||||
expect(localStorage.getItem(key)).toBe(value);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -25,11 +24,18 @@ export class StorageService {
|
||||
|
||||
private memoryStore: { [key: string]: any } = {};
|
||||
private useLocalStorage: boolean = false;
|
||||
storagePrefix: string;
|
||||
private _prefix: string = '';
|
||||
|
||||
constructor(private appConfigService: AppConfigService) {
|
||||
get prefix() {
|
||||
return this._prefix;
|
||||
}
|
||||
|
||||
set prefix(prefix: string) {
|
||||
this._prefix = prefix ? prefix + '_' : '';
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.useLocalStorage = this.storageAvailable('localStorage');
|
||||
this.appConfigService.onLoad.subscribe(this.getAppPrefix.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,9 +45,9 @@ export class StorageService {
|
||||
*/
|
||||
getItem(key: string): string | null {
|
||||
if (this.useLocalStorage) {
|
||||
return localStorage.getItem(this.storagePrefix + key);
|
||||
return localStorage.getItem(this.prefix + key);
|
||||
} else {
|
||||
return this.memoryStore.hasOwnProperty(this.storagePrefix + key) ? this.memoryStore[this.storagePrefix + key] : null;
|
||||
return this.memoryStore.hasOwnProperty(this.prefix + key) ? this.memoryStore[this.prefix + key] : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +58,9 @@ export class StorageService {
|
||||
*/
|
||||
setItem(key: string, data: string) {
|
||||
if (this.useLocalStorage) {
|
||||
localStorage.setItem(this.storagePrefix + key, data);
|
||||
localStorage.setItem(this.prefix + key, data);
|
||||
} else {
|
||||
this.memoryStore[this.storagePrefix + key] = data.toString();
|
||||
this.memoryStore[this.prefix + key] = data.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,9 +79,9 @@ export class StorageService {
|
||||
*/
|
||||
removeItem(key: string) {
|
||||
if (this.useLocalStorage) {
|
||||
localStorage.removeItem(this.storagePrefix + key);
|
||||
localStorage.removeItem(this.prefix + key);
|
||||
} else {
|
||||
delete this.memoryStore[this.storagePrefix + key];
|
||||
delete this.memoryStore[this.prefix + key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +92,7 @@ export class StorageService {
|
||||
*/
|
||||
hasItem(key: string): boolean {
|
||||
if (this.useLocalStorage) {
|
||||
return localStorage.getItem(this.storagePrefix + key) ? true : false;
|
||||
return localStorage.getItem(this.prefix + key) ? true : false;
|
||||
} else {
|
||||
return this.memoryStore.hasOwnProperty(key);
|
||||
}
|
||||
@@ -103,19 +109,4 @@ export class StorageService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix that is used for the local storage of the app
|
||||
* It assigns the string that is defined i the app config,
|
||||
* empty prefix otherwise.
|
||||
*/
|
||||
getAppPrefix() {
|
||||
const appConfiguration = this.appConfigService.get<any>('application');
|
||||
if (appConfiguration && appConfiguration.storagePrefix) {
|
||||
this.storagePrefix = appConfiguration.storagePrefix + '_';
|
||||
} else {
|
||||
this.storagePrefix = '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,6 +23,8 @@ import { UserPreferencesService, UserPreferenceValues } from './user-preferences
|
||||
import { setupTestBed } from '../testing/setupTestBed';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
import { AppConfigServiceMock } from '../mock/app-config.service.mock';
|
||||
import { AlfrescoApiService } from './alfresco-api.service';
|
||||
import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
|
||||
|
||||
describe('UserPreferencesService', () => {
|
||||
|
||||
@@ -30,13 +32,15 @@ describe('UserPreferencesService', () => {
|
||||
let preferences: UserPreferencesService;
|
||||
let storage: StorageService;
|
||||
let appConfig: AppConfigServiceMock;
|
||||
let alfrescoApiService: AlfrescoApiServiceMock;
|
||||
let translate: TranslateService;
|
||||
let changeDisposable: any;
|
||||
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [
|
||||
{ provide: AppConfigService, useClass: AppConfigServiceMock }
|
||||
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
|
||||
]
|
||||
});
|
||||
|
||||
@@ -51,6 +55,7 @@ describe('UserPreferencesService', () => {
|
||||
|
||||
storage = TestBed.get(StorageService);
|
||||
translate = TestBed.get(TranslateService);
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -65,176 +70,170 @@ describe('UserPreferencesService', () => {
|
||||
storage.clear();
|
||||
});
|
||||
|
||||
it('should get default pagination from app config', (done) => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
appConfig.config.pagination.size = 0;
|
||||
appConfig.load().then(() => {
|
||||
expect(preferences.paginationSize).toBe(0);
|
||||
done();
|
||||
describe(' with pagination config', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return supported page sizes defined in the app config', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
const supportedPages = preferences.supportedPageSizes;
|
||||
appConfig.load();
|
||||
expect(supportedPages).toEqual(supportedPaginationSize);
|
||||
});
|
||||
|
||||
it('should use [GUEST] as default storage prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.setStoragePrefix(null);
|
||||
expect(preferences.getStoragePrefix()).toBe('GUEST');
|
||||
});
|
||||
|
||||
it('should change storage prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.setStoragePrefix('USER_A');
|
||||
expect(preferences.getStoragePrefix()).toBe('USER_A');
|
||||
});
|
||||
|
||||
it('should format property key for default prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.setStoragePrefix(null);
|
||||
expect(preferences.getPropertyKey('propertyA')).toBe('GUEST__propertyA');
|
||||
});
|
||||
|
||||
it('should format property key for custom prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.setStoragePrefix('USER_A');
|
||||
expect(preferences.getPropertyKey('propertyA')).toBe('USER_A__propertyA');
|
||||
});
|
||||
|
||||
it('should save value with default prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.set('propertyA', 'valueA');
|
||||
const propertyKey = preferences.getPropertyKey('propertyA');
|
||||
expect(storage.getItem(propertyKey)).toBe('valueA');
|
||||
});
|
||||
|
||||
it('should null value return default prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
storage.setItem('paginationSize', null);
|
||||
const paginationSize = preferences.getPropertyKey('paginationSize');
|
||||
expect(preferences.get(paginationSize, 'default')).toBe('default');
|
||||
});
|
||||
|
||||
it('should save value with custom prefix', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.setStoragePrefix('USER_A');
|
||||
preferences.set('propertyA', 'valueA');
|
||||
const propertyKey = preferences.getPropertyKey('propertyA');
|
||||
expect(storage.getItem(propertyKey)).toBe('valueA');
|
||||
});
|
||||
|
||||
it('should return as default locale the app.config locate as first', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
appConfig.config.locale = 'fake-locate-config';
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.getDefaultLocale()).toBe('fake-locate-config');
|
||||
});
|
||||
|
||||
it('should return as default locale the browser locale as second', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.getDefaultLocale()).toBe('fake-locate-browser');
|
||||
});
|
||||
|
||||
it('should return as default locale the component property as third ', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
spyOn(translate, 'getBrowserCultureLang').and.stub();
|
||||
expect(preferences.getDefaultLocale()).toBe('en');
|
||||
});
|
||||
|
||||
it('should return as locale the store locate', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.locale = 'fake-store-locate';
|
||||
appConfig.config.locale = 'fake-locate-config';
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.locale).toBe('fake-store-locate');
|
||||
});
|
||||
|
||||
it('should store default textOrientation based on language ', async(() => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-config'
|
||||
}
|
||||
];
|
||||
appConfig.config.locale = 'fake-locale-config';
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
appConfig.load();
|
||||
const textOrientation = preferences.getPropertyKey('textOrientation');
|
||||
expect(storage.getItem(textOrientation)).toBe('ltr');
|
||||
}));
|
||||
|
||||
it('should store textOrientation based on language config direction', async(() => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-config',
|
||||
direction: 'rtl'
|
||||
}
|
||||
];
|
||||
appConfig.config.locale = 'fake-locale-config';
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
appConfig.load();
|
||||
const textOrientation = preferences.getPropertyKey('textOrientation');
|
||||
expect(storage.getItem(textOrientation)).toBe('rtl');
|
||||
}));
|
||||
|
||||
it('should not store textOrientation based on language ', async(() => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-browser'
|
||||
}
|
||||
];
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
appConfig.load();
|
||||
|
||||
const textOrientation = preferences.getPropertyKey('textOrientation');
|
||||
expect(storage.getItem(textOrientation)).toBe(null);
|
||||
}));
|
||||
|
||||
it('should default to browser locale for textOrientation when locale is not defined in configuration', (done) => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-browser',
|
||||
direction: 'rtl'
|
||||
}
|
||||
];
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locale-browser');
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
appConfig.load();
|
||||
|
||||
changeDisposable = preferences.onChange
|
||||
.subscribe((userPreferenceStatus) => {
|
||||
expect(userPreferenceStatus['textOrientation']).toBe('rtl');
|
||||
it('should get default pagination from app config', (done) => {
|
||||
appConfig.config.pagination.size = 0;
|
||||
appConfig.load().then(() => {
|
||||
expect(preferences.paginationSize).toBe(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return supported page sizes defined in the app config', () => {
|
||||
const supportedPages = preferences.supportedPageSizes;
|
||||
appConfig.load();
|
||||
expect(supportedPages).toEqual(supportedPaginationSize);
|
||||
});
|
||||
|
||||
it('should use [GUEST] as default storage prefix', () => {
|
||||
preferences.setStoragePrefix(null);
|
||||
expect(preferences.getStoragePrefix()).toBe('GUEST');
|
||||
});
|
||||
|
||||
it('should change storage prefix', () => {
|
||||
preferences.setStoragePrefix('USER_A');
|
||||
expect(preferences.getStoragePrefix()).toBe('USER_A');
|
||||
});
|
||||
|
||||
it('should format property key for default prefix', () => {
|
||||
preferences.setStoragePrefix(null);
|
||||
expect(preferences.getPropertyKey('propertyA')).toBe('GUEST__propertyA');
|
||||
});
|
||||
|
||||
it('should format property key for custom prefix', () => {
|
||||
preferences.setStoragePrefix('USER_A');
|
||||
expect(preferences.getPropertyKey('propertyA')).toBe('USER_A__propertyA');
|
||||
});
|
||||
|
||||
it('should save value with default prefix', () => {
|
||||
preferences.set('propertyA', 'valueA');
|
||||
const propertyKey = preferences.getPropertyKey('propertyA');
|
||||
expect(storage.getItem(propertyKey)).toBe('valueA');
|
||||
});
|
||||
|
||||
it('should null value return default prefix', () => {
|
||||
storage.setItem('paginationSize', null);
|
||||
const paginationSize = preferences.getPropertyKey('paginationSize');
|
||||
expect(preferences.get(paginationSize, 'default')).toBe('default');
|
||||
});
|
||||
|
||||
it('should save value with custom prefix', () => {
|
||||
preferences.setStoragePrefix('USER_A');
|
||||
preferences.set('propertyA', 'valueA');
|
||||
const propertyKey = preferences.getPropertyKey('propertyA');
|
||||
expect(storage.getItem(propertyKey)).toBe('valueA');
|
||||
});
|
||||
|
||||
it('should return as default locale the app.config locate as first', () => {
|
||||
appConfig.config.locale = 'fake-locate-config';
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.getDefaultLocale()).toBe('fake-locate-config');
|
||||
});
|
||||
|
||||
it('should return as default locale the browser locale as second', () => {
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.getDefaultLocale()).toBe('fake-locate-browser');
|
||||
});
|
||||
|
||||
it('should return as default locale the component property as third ', () => {
|
||||
spyOn(translate, 'getBrowserCultureLang').and.stub();
|
||||
expect(preferences.getDefaultLocale()).toBe('en');
|
||||
});
|
||||
|
||||
it('should return as locale the store locate', () => {
|
||||
preferences.locale = 'fake-store-locate';
|
||||
appConfig.config.locale = 'fake-locate-config';
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.locale).toBe('fake-store-locate');
|
||||
});
|
||||
|
||||
it('should not store in the storage the locale if the app.config.json does not have a value', () => {
|
||||
preferences.locale = 'fake-store-locate';
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.locale).toBe('fake-store-locate');
|
||||
expect(storage.getItem(UserPreferenceValues.Locale)).toBe(null);
|
||||
});
|
||||
|
||||
it('should stream the page size value when is set', (done) => {
|
||||
preferences.paginationSize = 5;
|
||||
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
|
||||
expect(userPreferenceStatus[UserPreferenceValues.PaginationSize]).toBe(5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should stream the user preference status when changed', (done) => {
|
||||
preferences.set('propertyA', 'valueA');
|
||||
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
|
||||
expect(userPreferenceStatus.propertyA).toBe('valueA');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not store in the storage the locale if the app.config.json does not have a value', () => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.locale = 'fake-store-locate';
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
|
||||
expect(preferences.locale).toBe('fake-store-locate');
|
||||
expect(storage.getItem(UserPreferenceValues.Locale)).toBe(null);
|
||||
});
|
||||
describe('with language config', () => {
|
||||
|
||||
it('should stream the page size value when is set', (done) => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.paginationSize = 5;
|
||||
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
|
||||
expect(userPreferenceStatus[UserPreferenceValues.PaginationSize]).toBe(5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should store default textOrientation based on language', async(() => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-config'
|
||||
}
|
||||
];
|
||||
appConfig.config.locale = 'fake-locale-config';
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
|
||||
alfrescoApiService.initialize();
|
||||
const textOrientation = preferences.getPropertyKey('textOrientation');
|
||||
expect(storage.getItem(textOrientation)).toBe('ltr');
|
||||
}));
|
||||
|
||||
it('should stream the user preference status when changed', (done) => {
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage);
|
||||
preferences.set('propertyA', 'valueA');
|
||||
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
|
||||
expect(userPreferenceStatus.propertyA).toBe('valueA');
|
||||
done();
|
||||
it('should store textOrientation based on language config direction', async(() => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-config',
|
||||
direction: 'rtl'
|
||||
}
|
||||
];
|
||||
appConfig.config.locale = 'fake-locale-config';
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
|
||||
alfrescoApiService.initialize();
|
||||
const textOrientation = preferences.getPropertyKey('textOrientation');
|
||||
expect(storage.getItem(textOrientation)).toBe('rtl');
|
||||
}));
|
||||
|
||||
it('should not store textOrientation based on language ', async(() => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-browser'
|
||||
}
|
||||
];
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
|
||||
alfrescoApiService.initialize();
|
||||
|
||||
const textOrientation = preferences.getPropertyKey('textOrientation');
|
||||
expect(storage.getItem(textOrientation)).toBe(null);
|
||||
}));
|
||||
|
||||
it('should default to browser locale for textOrientation when locale is not defined in configuration', (done) => {
|
||||
appConfig.config.languages = [
|
||||
{
|
||||
key: 'fake-locale-browser',
|
||||
direction: 'rtl'
|
||||
}
|
||||
];
|
||||
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locale-browser');
|
||||
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
|
||||
alfrescoApiService.initialize();
|
||||
|
||||
changeDisposable = preferences.onChange
|
||||
.subscribe((userPreferenceStatus) => {
|
||||
expect(userPreferenceStatus['textOrientation']).toBe('rtl');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -22,6 +22,7 @@ import { AppConfigService, AppConfigValues } from '../app-config/app-config.serv
|
||||
import { LanguageItem } from '../language-menu/language.interface';
|
||||
import { StorageService } from './storage.service';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { AlfrescoApiService } from './alfresco-api.service';
|
||||
|
||||
export enum UserPreferenceValues {
|
||||
PaginationSize = 'paginationSize',
|
||||
@@ -48,8 +49,9 @@ export class UserPreferencesService {
|
||||
|
||||
constructor(public translate: TranslateService,
|
||||
private appConfig: AppConfigService,
|
||||
private storage: StorageService) {
|
||||
this.appConfig.onLoad.subscribe(this.initUserPreferenceStatus.bind(this));
|
||||
private storage: StorageService,
|
||||
private alfrescoApiService: AlfrescoApiService) {
|
||||
this.alfrescoApiService.alfrescoApiInitialized.subscribe(this.initUserPreferenceStatus.bind(this));
|
||||
this.onChangeSubject = new BehaviorSubject(this.userPreferenceStatus);
|
||||
this.onChange = this.onChangeSubject.asObservable();
|
||||
}
|
||||
|
Reference in New Issue
Block a user