Skip to content

Commit

Permalink
added new feature toggle and guard to hide get audio file
Browse files Browse the repository at this point in the history
  • Loading branch information
marcogagliardi committed Nov 16, 2023
1 parent 4ab85e7 commit 4563b57
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { WorkAllocationFeatureGuard } from './security/work-allocation-feature.g
import { HomeComponent } from './home/home.component';
import { AuthGuard } from './security/auth.guard';
import { ManageTeamFeatureGuard } from './security/manage-team-feature.guard';
import { AudioSearchGuard } from './security/audio-search.guard';

export const routes: Routes = [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
Expand All @@ -28,7 +29,7 @@ export const routes: Routes = [
{ path: 'error', component: ErrorComponent },
{ path: 'unsupported-browser', component: UnsupportedBrowserComponent },
{ path: 'change-password', component: ChangePasswordComponent, canActivate: [AuthGuard, AdminGuard] },
{ path: 'get-audio-file', component: GetAudioFileComponent, canActivate: [AuthGuard, AdminGuard] },
{ path: 'get-audio-file', component: GetAudioFileComponent, canActivate: [AuthGuard, AdminGuard, AudioSearchGuard] },
{ path: 'delete-participant', component: DeleteParticipantSearchComponent, canActivate: [AuthGuard, AdminGuard] },
{ path: 'edit-participant-search', component: EditParticipantSearchComponent, canActivate: [AuthGuard, AdminGuard] },
{ path: 'edit-participant', component: EditParticipantComponent, canActivate: [AuthGuard, AdminGuard] },
Expand Down
4 changes: 3 additions & 1 deletion AdminWebsite/AdminWebsite/ClientApp/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { UnallocatedHearingsComponent } from './dashboard/unallocated-hearings/unallocated-hearings.component';
import { HomeComponent } from './home/home.component';
import { ManageTeamFeatureGuard } from './security/manage-team-feature.guard';
import { AudioSearchGuard } from './security/audio-search.guard';

export function loadConfig(configService: ConfigService) {
return () => configService.loadConfig();
Expand Down Expand Up @@ -99,7 +100,8 @@ export function loadConfig(configService: ConfigService) {
AppInsightsLogger,
WindowRef,
WorkAllocationFeatureGuard,
ManageTeamFeatureGuard
ManageTeamFeatureGuard,
AudioSearchGuard
],
exports: [UnallocatedHearingsComponent],
bootstrap: [AppComponent]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
showWorkAllocation = false;
vhoWorkAllocationFeature = false;
dom1Feature = false;
hrsIntegrationFeature: boolean;
audioSearchFeature: boolean;

showManageTeam = false;
showAudioFileLink = false;
Expand All @@ -37,18 +37,23 @@ export class DashboardComponent implements OnInit, OnDestroy {
const workAllocationFlag$ = this.launchDarklyService
.getFlag<boolean>(FeatureFlags.vhoWorkAllocation)
.pipe(takeUntil(this.destroyed$));
const hrsIntegrationFlag$ = this.launchDarklyService.getFlag<boolean>(FeatureFlags.hrsIntegration).pipe(takeUntil(this.destroyed$));
const dom1FeatureFlag$ = this.launchDarklyService.getFlag<boolean>(FeatureFlags.dom1Integration).pipe(takeUntil(this.destroyed$));
const audioSearchFlag$ = this.launchDarklyService
.getFlag<boolean>(FeatureFlags.audioSearch)
.pipe(takeUntil(this.destroyed$));
const dom1FeatureFlag$ = this.launchDarklyService
.getFlag<boolean>(FeatureFlags.dom1Integration)
.pipe(takeUntil(this.destroyed$));

combineLatest([workAllocationFlag$, hrsIntegrationFlag$, dom1FeatureFlag$]).subscribe(
([workAllocationFlag, hrsIntegrationFlag, dom1FeatureFlag]) => {
combineLatest([workAllocationFlag$, audioSearchFlag$, dom1FeatureFlag$]).subscribe(
([workAllocationFlag, audioSearchFlag, dom1FeatureFlag]) => {
this.vhoWorkAllocationFeature = workAllocationFlag;
this.hrsIntegrationFeature = hrsIntegrationFlag;
this.audioSearchFeature = audioSearchFlag;
console.log('################### ' + audioSearchFlag);
this.dom1Feature = dom1FeatureFlag;
lastValueFrom(this.userIdentityService.getUserInformation()).then(profile => {
this.showCheckList = profile.is_vh_officer_administrator_role;
this.showWorkAllocation = profile.is_vh_team_leader && this.vhoWorkAllocationFeature;
this.showAudioFileLink = this.showCheckList && !this.hrsIntegrationFeature;
this.showAudioFileLink = this.showCheckList && !this.audioSearchFeature;
this.showBooking = profile.is_case_administrator || profile.is_vh_officer_administrator_role;
this.showManageTeam = profile.is_vh_team_leader && this.dom1Feature;
this.logger.debug(`${this.loggerPrefix} Landed on dashboard`, {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { fakeAsync, TestBed } from '@angular/core/testing';
import { AuthGuard } from './auth.guard';
import { Router } from '@angular/router';
import { MockOidcSecurityService } from '../testing/mocks/MockOidcSecurityService';
import { OidcSecurityService } from 'angular-auth-oidc-client';
import { Logger } from '../services/logger';
import { AudioSearchGuard } from './audio-search.guard';
import { LaunchDarklyService } from '../services/launch-darkly.service';
import { MockLaunchDarklyService } from '../testing/mocks/MockLaunchDarklyService';

describe('audiosearchguard', () => {
let audioSearchGuard: AudioSearchGuard;
let launchDarklyService;
const loggerSpy = jasmine.createSpyObj<Logger>('Logger', ['error', 'debug', 'warn']);
const router = {
navigate: jasmine.createSpy('navigate')
};

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
AuthGuard,
{ provide: LaunchDarklyService, useClass: MockLaunchDarklyService },
{ provide: Router, useValue: router },
{ provide: Logger, useValue: loggerSpy }
]
}).compileComponents();
launchDarklyService = TestBed.inject(LaunchDarklyService);
audioSearchGuard = TestBed.inject(AudioSearchGuard);
});

describe('when logged in with successful authentication', () => {
it('canActivate should return true', () => {
launchDarklyService.setAudioSearchFlag(true);
audioSearchGuard.canActivate().subscribe(result => expect(result).toBeTruthy());
});
});

describe('when login failed with unsuccessful authentication', () => {
it('canActivate should return false', fakeAsync(() => {
launchDarklyService.setAudioSearchFlag(false);
audioSearchGuard.canActivate().subscribe(result => expect(result).toBeFalsy());
}));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthenticatedResult, OidcSecurityService } from 'angular-auth-oidc-client';
import { Observable, takeUntil } from 'rxjs';
import { map } from 'rxjs/operators';
import { PageUrls } from '../shared/page-url.constants';
import { Logger } from '../services/logger';
import { FeatureFlags, LaunchDarklyService } from '../services/launch-darkly.service';

@Injectable()
export class AudioSearchGuard implements CanActivate {
private readonly loggerPrefix = '[AudioSearchGuard] -';
constructor(private launchDarklyService: LaunchDarklyService, private router: Router, private logger: Logger) {}

canActivate(): Observable<boolean> {
return this.launchDarklyService
.getFlag<boolean>(FeatureFlags.audioSearch)
.pipe(
map((result) => {
if (result) {
this.logger.warn(`${this.loggerPrefix} - canActivate isAuthorized: ` + result);
this.router.navigate([`/${PageUrls.Login}`]);
return false;
}
this.logger.debug(`${this.loggerPrefix} - canActivate isAuthorized: ` + result);
return true;
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export const FeatureFlags = {
eJudFeature: 'ejud-feature',
dom1Integration: 'dom1',
hrsIntegration: 'hrs-integration',
referenceData: 'reference-data'
referenceData: 'reference-data',
audioSearch: 'hide-audio-search-tile'
};

@Injectable({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { OpenIdConfiguration, LoginResponse, AuthenticatedResult, ConfigAuthenticatedResult } from 'angular-auth-oidc-client';
import { LDFlagValue } from 'launchdarkly-js-client-sdk';

class MockLoginResponse implements LoginResponse {
constructor(isAuthenticated: boolean) {
this.isAuthenticated = isAuthenticated;
}

isAuthenticated: boolean;
userData: any;
accessToken: string;
idToken: string;
configId: string;
errorMessage?: string;
}
export class MockLaunchDarklyService {
audioSearchFlag: boolean;

setAudioSearchFlag(flag: boolean) {
this.audioSearchFlag = flag;
}
getFlag<T>(flagKey: string, defaultValue: LDFlagValue = false): T {
return this.audioSearchFlag as T;
}
}
8 changes: 8 additions & 0 deletions AdminWebsite/AdminWebsite/Configuration/FeatureToggles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface IFeatureToggles
public bool ReferenceDataToggle();
public bool EJudEnabled();
public bool HrsEnabled();
public bool AudioSearchEnabled();
}

public class FeatureToggles : IFeatureToggles
Expand All @@ -25,6 +26,8 @@ public class FeatureToggles : IFeatureToggles
private const string ReferenceDataToggleKey = "reference-data";
private const string EJudFeatureToggleKey = "ejud-feature";
private const string HrsFeatureToggleKey = "hrs-integration";
private const string AudioSearchToggleKey = "hide-audio-search-tile";


public FeatureToggles(string sdkKey, string environmentName)
{
Expand Down Expand Up @@ -59,6 +62,11 @@ public bool HrsEnabled()
return GetBoolValueWithKey(HrsFeatureToggleKey);
}

public bool AudioSearchEnabled()
{
return GetBoolValueWithKey(AudioSearchToggleKey);
}

private bool GetBoolValueWithKey(string key)
{
if (!_ldClient.Initialized)
Expand Down

0 comments on commit 4563b57

Please sign in to comment.