mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
AAE-19688 Persist feature flag overrides in session storage (#10832)
* AAE-19688 Persist feature flag overrides in session storage * unit tests
This commit is contained in:
parent
61a1fb64bf
commit
9ee0e5ee3e
@ -17,37 +17,37 @@
|
|||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { DebugFeaturesService } from './debug-features.service';
|
import { DebugFeaturesService } from './debug-features.service';
|
||||||
import { StorageService } from '../../../../src/lib/common/services/storage.service';
|
import { OverridableFeaturesServiceToken, WritableFeaturesServiceConfigToken, WritableFeaturesServiceToken } from '../interfaces/features.interface';
|
||||||
import { OverridableFeaturesServiceToken, WritableFeaturesServiceToken } from '../interfaces/features.interface';
|
|
||||||
import { DummyFeaturesService } from './dummy-features.service';
|
import { DummyFeaturesService } from './dummy-features.service';
|
||||||
import { StorageFeaturesService } from './storage-features.service';
|
import { StorageFeaturesService } from './storage-features.service';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
describe('DebugFeaturesService', () => {
|
describe('DebugFeaturesService', () => {
|
||||||
let service: DebugFeaturesService;
|
let service: DebugFeaturesService;
|
||||||
const mockStorage = {
|
let mockStorageKey: string;
|
||||||
getItem: () =>
|
let mockStorage;
|
||||||
JSON.stringify({
|
|
||||||
feature1: {
|
|
||||||
current: true
|
|
||||||
},
|
|
||||||
feature2: {
|
|
||||||
current: false,
|
|
||||||
fictive: true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
setItem: () => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mockStorageKey = 'storage-key-test';
|
||||||
|
mockStorage = { [mockStorageKey]: true };
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
DebugFeaturesService,
|
DebugFeaturesService,
|
||||||
{ provide: StorageService, useValue: mockStorage },
|
{
|
||||||
|
provide: WritableFeaturesServiceConfigToken,
|
||||||
|
useValue: { storageKey: mockStorageKey }
|
||||||
|
},
|
||||||
{ provide: WritableFeaturesServiceToken, useClass: StorageFeaturesService },
|
{ provide: WritableFeaturesServiceToken, useClass: StorageFeaturesService },
|
||||||
{ provide: OverridableFeaturesServiceToken, useClass: DummyFeaturesService }
|
{ provide: OverridableFeaturesServiceToken, useClass: DummyFeaturesService }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
spyOn(sessionStorage, 'getItem').and.callFake((key) => JSON.stringify(mockStorage[key]));
|
||||||
|
spyOn(sessionStorage, 'setItem').and.callFake((key, value) => {
|
||||||
|
mockStorage[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
service = TestBed.inject(DebugFeaturesService);
|
service = TestBed.inject(DebugFeaturesService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable, Optional } from '@angular/core';
|
import { Inject, Injectable, Optional } from '@angular/core';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
|
||||||
import { skip, switchMap } from 'rxjs/operators';
|
import { filter, switchMap } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
IDebugFeaturesService,
|
IDebugFeaturesService,
|
||||||
IFeaturesService,
|
IFeaturesService,
|
||||||
@ -29,12 +29,12 @@ import {
|
|||||||
FlagSet,
|
FlagSet,
|
||||||
IWritableFeaturesService
|
IWritableFeaturesService
|
||||||
} from '../interfaces/features.interface';
|
} from '../interfaces/features.interface';
|
||||||
import { StorageService } from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DebugFeaturesService implements IDebugFeaturesService {
|
export class DebugFeaturesService implements IDebugFeaturesService {
|
||||||
private isInDebugMode: BehaviorSubject<boolean>;
|
private readonly isInDebugModeSubject = new BehaviorSubject<boolean>(false);
|
||||||
private isInDebugMode$: Observable<boolean>;
|
private readonly isInDebugMode$ = this.isInDebugModeSubject.asObservable();
|
||||||
|
private readonly initSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
get storageKey(): string {
|
get storageKey(): string {
|
||||||
return `${this.config?.storageKey || 'feature-flags'}-override`;
|
return `${this.config?.storageKey || 'feature-flags'}-override`;
|
||||||
@ -43,14 +43,15 @@ export class DebugFeaturesService implements IDebugFeaturesService {
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(OverridableFeaturesServiceToken) private overriddenFeaturesService: IFeaturesService,
|
@Inject(OverridableFeaturesServiceToken) private overriddenFeaturesService: IFeaturesService,
|
||||||
@Inject(WritableFeaturesServiceToken) private writableFeaturesService: IFeaturesService & IWritableFeaturesService,
|
@Inject(WritableFeaturesServiceToken) private writableFeaturesService: IFeaturesService & IWritableFeaturesService,
|
||||||
private storageService: StorageService,
|
|
||||||
@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig
|
@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig
|
||||||
) {
|
) {
|
||||||
this.isInDebugMode = new BehaviorSubject<boolean>(JSON.parse(this.storageService.getItem(this.storageKey) || 'false'));
|
this.init();
|
||||||
this.isInDebugMode$ = this.isInDebugMode.asObservable();
|
|
||||||
|
|
||||||
this.isInDebugMode.pipe(skip(1)).subscribe((debugMode) => {
|
combineLatest({
|
||||||
this.storageService.setItem(this.storageKey, JSON.stringify(debugMode));
|
debugMode: this.isInDebugModeSubject,
|
||||||
|
init: this.waitForInitializationToFinish()
|
||||||
|
}).subscribe(({ debugMode }) => {
|
||||||
|
sessionStorage.setItem(this.storageKey, JSON.stringify(debugMode));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,10 +88,20 @@ export class DebugFeaturesService implements IDebugFeaturesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enable(on: boolean): void {
|
enable(on: boolean): void {
|
||||||
this.isInDebugMode.next(on);
|
this.isInDebugModeSubject.next(on);
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnabled$(): Observable<boolean> {
|
isEnabled$(): Observable<boolean> {
|
||||||
return this.isInDebugMode$;
|
return this.isInDebugMode$;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
const storedOverride = JSON.parse(sessionStorage.getItem(this.storageKey) || 'false');
|
||||||
|
this.isInDebugModeSubject.next(storedOverride);
|
||||||
|
this.initSubject.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private waitForInitializationToFinish(): Observable<boolean> {
|
||||||
|
return this.initSubject.pipe(filter((initialized) => !!initialized));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,20 @@
|
|||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { StorageFeaturesService } from './storage-features.service';
|
import { StorageFeaturesService } from './storage-features.service';
|
||||||
import { StorageService } from '../../../../src/public-api';
|
|
||||||
import { FlagSet, WritableFeaturesServiceConfigToken } from '../interfaces/features.interface';
|
import { FlagSet, WritableFeaturesServiceConfigToken } from '../interfaces/features.interface';
|
||||||
import { skip, take } from 'rxjs/operators';
|
import { skip, take } from 'rxjs/operators';
|
||||||
|
|
||||||
describe('StorageFeaturesService', () => {
|
describe('StorageFeaturesService', () => {
|
||||||
let storageFeaturesService: StorageFeaturesService;
|
let storageFeaturesService: StorageFeaturesService;
|
||||||
|
|
||||||
describe('if flags are present in LocalStorage', () => {
|
describe('if flags are present in sessionStorage', () => {
|
||||||
const mockStorage = {
|
let mockStorageKey: string;
|
||||||
getItem: () =>
|
let mockStorage;
|
||||||
JSON.stringify({
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockStorageKey = 'storage-key-test';
|
||||||
|
mockStorage = {
|
||||||
|
[mockStorageKey]: {
|
||||||
feature1: {
|
feature1: {
|
||||||
current: true
|
current: true
|
||||||
},
|
},
|
||||||
@ -35,23 +38,23 @@ describe('StorageFeaturesService', () => {
|
|||||||
current: false,
|
current: false,
|
||||||
fictive: true
|
fictive: true
|
||||||
}
|
}
|
||||||
}),
|
}
|
||||||
setItem: () => {}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: StorageService, useValue: mockStorage },
|
|
||||||
{
|
{
|
||||||
provide: WritableFeaturesServiceConfigToken,
|
provide: WritableFeaturesServiceConfigToken,
|
||||||
useValue: {
|
useValue: { storageKey: mockStorageKey }
|
||||||
storageKey: 'storage-key-test'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
spyOn(sessionStorage, 'getItem').and.callFake((key) => JSON.stringify(mockStorage[key]));
|
||||||
|
spyOn(sessionStorage, 'setItem').and.callFake((key, value) => {
|
||||||
|
mockStorage[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
storageFeaturesService = TestBed.inject(StorageFeaturesService);
|
storageFeaturesService = TestBed.inject(StorageFeaturesService);
|
||||||
storageFeaturesService.init();
|
storageFeaturesService.init();
|
||||||
});
|
});
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable, Optional } from '@angular/core';
|
import { Inject, Injectable, Optional } from '@angular/core';
|
||||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
|
||||||
import { map, skip } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
FlagChangeset,
|
FlagChangeset,
|
||||||
IFeaturesService,
|
IFeaturesService,
|
||||||
@ -28,21 +28,21 @@ import {
|
|||||||
WritableFeaturesServiceConfig
|
WritableFeaturesServiceConfig
|
||||||
} from '../interfaces/features.interface';
|
} from '../interfaces/features.interface';
|
||||||
import { FlagSetParser } from './flagset.parser';
|
import { FlagSetParser } from './flagset.parser';
|
||||||
import { StorageService } from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class StorageFeaturesService implements IFeaturesService, IWritableFeaturesService {
|
export class StorageFeaturesService implements IFeaturesService, IWritableFeaturesService {
|
||||||
private currentFlagState: WritableFlagChangeset = {};
|
private currentFlagState: WritableFlagChangeset = {};
|
||||||
private flags = new BehaviorSubject<WritableFlagChangeset>({});
|
private readonly flags = new BehaviorSubject<WritableFlagChangeset>({});
|
||||||
private flags$ = this.flags.asObservable();
|
private readonly flags$ = this.flags.asObservable();
|
||||||
|
private readonly initSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
constructor(
|
constructor(@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig) {
|
||||||
private storageService: StorageService,
|
combineLatest({
|
||||||
@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig
|
flags: this.flags,
|
||||||
) {
|
init: this.waitForInitializationToFinish()
|
||||||
this.flags.pipe(skip(1)).subscribe((flags) => {
|
}).subscribe(({ flags }) => {
|
||||||
this.currentFlagState = flags;
|
this.currentFlagState = flags;
|
||||||
this.storageService.setItem(this.storageKey, JSON.stringify(FlagSetParser.serialize(flags)));
|
sessionStorage.setItem(this.storageKey, JSON.stringify(FlagSetParser.serialize(flags)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,9 +51,10 @@ export class StorageFeaturesService implements IFeaturesService, IWritableFeatur
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(): Observable<WritableFlagChangeset> {
|
init(): Observable<WritableFlagChangeset> {
|
||||||
const storedFlags = JSON.parse(this.storageService.getItem(this.storageKey) || '{}');
|
const storedFlags = JSON.parse(sessionStorage.getItem(this.storageKey) || '{}');
|
||||||
const initialFlagChangeSet = FlagSetParser.deserialize(storedFlags);
|
const initialFlagChangeSet = FlagSetParser.deserialize(storedFlags);
|
||||||
this.flags.next(initialFlagChangeSet);
|
this.flags.next(initialFlagChangeSet);
|
||||||
|
this.initSubject.next(true);
|
||||||
return of(initialFlagChangeSet);
|
return of(initialFlagChangeSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,4 +134,8 @@ export class StorageFeaturesService implements IFeaturesService, IWritableFeatur
|
|||||||
|
|
||||||
this.flags.next(mergedFlags);
|
this.flags.next(mergedFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private waitForInitializationToFinish(): Observable<boolean> {
|
||||||
|
return this.initSubject.pipe(filter((initialized) => !!initialized));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user