diff --git a/package-lock.json b/package-lock.json index cb4c21fa5..62eff1ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13225,6 +13225,12 @@ "path-key": "^2.0.0" } }, + "npm-user-validate": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=", + "dev": true + }, "npmlog": { "version": "4.1.2", "resolved": false, @@ -20756,7 +20762,7 @@ "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", + "serialize-javascript": "^3.1.0", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 90c6b6463..0680b43f9 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/app/modules/login/services/azure.ad.b2c.service.spec.ts b/src/app/modules/login/services/azure.ad.b2c.service.spec.ts index b395d3bdd..97c7f35c9 100644 --- a/src/app/modules/login/services/azure.ad.b2c.service.spec.ts +++ b/src/app/modules/login/services/azure.ad.b2c.service.spec.ts @@ -1,6 +1,7 @@ -import { TestBed, inject } from '@angular/core/testing'; +import { inject, TestBed } from '@angular/core/testing'; +import { Account, UserAgentApplication } from 'msal'; import { AzureAdB2CService } from './azure.ad.b2c.service'; -import { UserAgentApplication, Account } from 'msal'; + describe('AzureAdB2CService', () => { let service: AzureAdB2CService; @@ -130,4 +131,20 @@ describe('AzureAdB2CService', () => { expect(sessionStorage.getItem).toHaveBeenCalled(); expect(resp).toEqual(token); }); + + it('should get email from UserAgentApplication', () => { + spyOn(UserAgentApplication.prototype, 'getAccount').and.returnValues(account); + + const name = service.getName(); + + expect(UserAgentApplication.prototype.getAccount).toHaveBeenCalled(); + }); + + it('should group from UserAgentApplication', () => { + spyOn(UserAgentApplication.prototype, 'getAccount').and.returnValues(account); + + const name = service.getUserGroup(); + + expect(UserAgentApplication.prototype.getAccount).toHaveBeenCalled(); + }); }); diff --git a/src/app/modules/login/services/azure.ad.b2c.service.ts b/src/app/modules/login/services/azure.ad.b2c.service.ts index 2a0239fd6..b6945f9de 100644 --- a/src/app/modules/login/services/azure.ad.b2c.service.ts +++ b/src/app/modules/login/services/azure.ad.b2c.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; -import { Observable, from } from 'rxjs'; -import { CLIENT_ID, AUTHORITY, SCOPES } from '../../../../environments/environment'; import { UserAgentApplication } from 'msal'; +import { from, Observable } from 'rxjs'; + +import { AUTHORITY, CLIENT_ID, SCOPES } from '../../../../environments/environment'; @Injectable({ providedIn: 'root', @@ -55,4 +56,12 @@ export class AzureAdB2CService { getBearerToken(): string { return sessionStorage.getItem('msal.idtoken'); } + + getUserEmail(): string { + return this.msal.getAccount().idToken?.emails[0]; + } + + getUserGroup(): string { + return this.msal.getAccount().idToken?.extension_role; + } } diff --git a/src/app/modules/reports/pages/reports.component.ts b/src/app/modules/reports/pages/reports.component.ts index bb276a460..dbdc3e14c 100644 --- a/src/app/modules/reports/pages/reports.component.ts +++ b/src/app/modules/reports/pages/reports.component.ts @@ -1,15 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; @Component({ selector: 'app-reports', templateUrl: './reports.component.html', styleUrls: ['./reports.component.scss'] }) -export class ReportsComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { - } - +export class ReportsComponent { } diff --git a/src/app/modules/shared/feature-toggles/feature-toggle-configuration.ts b/src/app/modules/shared/feature-toggles/feature-toggle-configuration.ts new file mode 100644 index 000000000..865a833b8 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/feature-toggle-configuration.ts @@ -0,0 +1,10 @@ +import { FeatureFilterConfiguration } from './filters/feature-filter-configuration'; + +export interface FeatureToggleConfiguration { + id: string; + enabled: boolean; + description: string; + conditions: { + client_filters: FeatureFilterConfiguration[]; + }; +} diff --git a/src/app/modules/shared/feature-toggles/feature-toggle-manager.service.spec.ts b/src/app/modules/shared/feature-toggles/feature-toggle-manager.service.spec.ts new file mode 100644 index 000000000..471d10f62 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/feature-toggle-manager.service.spec.ts @@ -0,0 +1,94 @@ +import { AppConfigurationClient } from '@azure/app-configuration'; +import { of } from 'rxjs'; +import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service'; +import { FeatureManagerService } from './feature-toggle-manager.service'; +import { FeatureToggleProvider } from './feature-toggle-provider.service'; +import { FeatureToggleModel } from './feature-toggle.model'; +import { FeatureFilterProvider } from './filters/feature-filter-provider.service'; +import { TargetingFeatureFilterModel } from './filters/targeting/targeting-feature-filter.model'; + + +describe('FeatureToggleManager', () => { + const featureToggleKey = 'foo'; + const featureToggleLabel = 'dev'; + const fakeAppConfigurationConnectionString = 'Endpoint=http://fake.foo;Id=fake.id;Secret=fake.secret'; + const aFeatureToggle = new FeatureToggleModel('any-id', true, []); + let service: FeatureManagerService; + let fakeFeatureToggleProvider; + + describe('Features without filters', () => { + beforeEach(() => { + + fakeFeatureToggleProvider = new FeatureToggleProvider( + new AppConfigurationClient(fakeAppConfigurationConnectionString), + new FeatureFilterProvider(new AzureAdB2CService()) + ); + spyOn(fakeFeatureToggleProvider, 'getFeatureToggle').and.returnValue(of(aFeatureToggle)); + service = new FeatureManagerService(fakeFeatureToggleProvider); + }); + it('manager uses feature provider to build feature toggle model', async () => { + service.isToggleEnabled(featureToggleKey, featureToggleLabel).subscribe((value) => { + + expect(fakeFeatureToggleProvider).toHaveBeenCalledWith(featureToggleKey, featureToggleLabel); + }); + }); + + it('manager extracts enabled attribute from feature toggle model', async () => { + service.isToggleEnabled(featureToggleKey, featureToggleLabel).subscribe((value) => { + expect(value).toEqual(aFeatureToggle.enabled); + }); + }); + }); + + + describe('Features with filters', () => { + const anyMatchingFilter = new TargetingFeatureFilterModel( + { Audience: { Groups: ['group-a'], Users: ['user-a'] } }, + { username: 'user-b', group: 'group-a' } + ); + const anyNotMatchingFilter = new TargetingFeatureFilterModel( + { Audience: { Groups: ['a-group'], Users: ['user-a'] } }, + { username: 'user-b', group: 'b-group' } + ); + + let aToggleWithFilters; + let getFeatureToggleSpy; + + beforeEach(() => { + aToggleWithFilters = new FeatureToggleModel('any-other-id', true, [anyMatchingFilter]); + fakeFeatureToggleProvider = new FeatureToggleProvider( + new AppConfigurationClient(fakeAppConfigurationConnectionString), + new FeatureFilterProvider(new AzureAdB2CService()) + ); + getFeatureToggleSpy = spyOn(fakeFeatureToggleProvider, 'getFeatureToggle').and.returnValue(of(aToggleWithFilters)); + service = new FeatureManagerService(fakeFeatureToggleProvider); + }); + + it('manager uses feature provider to build feature toggle model', async () => { + service.isToggleEnabledForUser(featureToggleKey, featureToggleLabel).subscribe((value) => { + expect(getFeatureToggleSpy).toHaveBeenCalledWith(featureToggleKey, featureToggleLabel); + }); + }); + + it('given a feature toggle with filters which match the verification, then the response is true', async () => { + service.isToggleEnabledForUser(featureToggleKey, featureToggleLabel).subscribe((value) => { + expect(value).toEqual(true); + }); + }); + + it('given a feature toggle with filters which do not match the verification, then the response is false', async () => { + + aToggleWithFilters = new FeatureToggleModel('any-other-id', true, [anyNotMatchingFilter]); + fakeFeatureToggleProvider = new FeatureToggleProvider( + new AppConfigurationClient(fakeAppConfigurationConnectionString), + new FeatureFilterProvider(new AzureAdB2CService()) + ); + spyOn(fakeFeatureToggleProvider, 'getFeatureToggle').and.returnValue(of(aToggleWithFilters)); + service = new FeatureManagerService(fakeFeatureToggleProvider); + + service.isToggleEnabledForUser(featureToggleKey, featureToggleLabel).subscribe((value) => { + expect(value).toEqual(false); + }); + }); + }); +}); diff --git a/src/app/modules/shared/feature-toggles/feature-toggle-manager.service.ts b/src/app/modules/shared/feature-toggles/feature-toggle-manager.service.ts new file mode 100644 index 000000000..263b40e6c --- /dev/null +++ b/src/app/modules/shared/feature-toggles/feature-toggle-manager.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { FeatureToggleProvider } from './feature-toggle-provider.service'; + + +@Injectable({ + providedIn: 'root', +}) +export class FeatureManagerService { + + constructor(private featureToggleProvider: FeatureToggleProvider) { } + + public isToggleEnabled(toggleName: string, toggleLabel?: string): Observable { + return this.featureToggleProvider.getFeatureToggle(toggleName, toggleLabel).pipe( + map(featureToggle => featureToggle.enabled) + ); + } + + public isToggleEnabledForUser(toggleName: string, toggleLabel?: string): Observable { + return this.featureToggleProvider.getFeatureToggle(toggleName, toggleLabel).pipe( + map(featureToggle => featureToggle.filters), + map(filters => filters.map(filter => filter.evaluate())), + map(filterEvaluations => filterEvaluations.includes(true)) + ); + } +} diff --git a/src/app/modules/shared/feature-toggles/feature-toggle-provider.service.spec.ts b/src/app/modules/shared/feature-toggles/feature-toggle-provider.service.spec.ts new file mode 100644 index 000000000..5c6d9b5e4 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/feature-toggle-provider.service.spec.ts @@ -0,0 +1,80 @@ +import { AppConfigurationClient, GetConfigurationSettingResponse } from '@azure/app-configuration'; +import { of } from 'rxjs'; +import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service'; +import { FeatureToggleConfiguration } from './feature-toggle-configuration'; +import { FeatureToggleProvider } from './feature-toggle-provider.service'; +import { FeatureToggleModel } from './feature-toggle.model'; +import { FeatureFilterProvider } from './filters/feature-filter-provider.service'; +import { TargetingFeatureFilterModel } from './filters/targeting/targeting-feature-filter.model'; + + +describe('FeatureToggleProvider', () => { + const anyToggleResponse: FeatureToggleConfiguration = { + id: '1', + enabled: true, + description: 'any description', + conditions: { + client_filters: [{ name: 'any-name', parameters: {} }] + }, + }; + + const fakeAppConfigurationConnectionString = 'Endpoint=http://fake.foo;Id=fake.id;Secret=fake.secret'; + const featureToggleKey = 'foo'; + const featureToggleLabel = 'dev'; + const fakeResponse: GetConfigurationSettingResponse = { + isReadOnly: true, + key: featureToggleKey, + _response: { + request: null, + bodyAsText: 'any-response', + headers: null, + parsedHeaders: null, + status: 200 + }, + statusCode: 200, + value: JSON.stringify(anyToggleResponse) + }; + const anyFilter = new TargetingFeatureFilterModel( + { Audience: { Groups: ['a-group'], Users: ['any-user'] } }, + { username: 'fakeuser@ioet.com', group: 'fake-group' } + ); + let fakeConfigurationClient; + let fakeGetConfigurationSetting; + let fakeFeatureFilterProvider; + let getFilterConfigurationSpy; + let service; + + + + beforeEach(() => { + fakeConfigurationClient = new AppConfigurationClient(fakeAppConfigurationConnectionString); + fakeGetConfigurationSetting = spyOn(fakeConfigurationClient, 'getConfigurationSetting').and.callFake( + () => of(fakeResponse).toPromise()); + + fakeFeatureFilterProvider = new FeatureFilterProvider(new AzureAdB2CService()); + getFilterConfigurationSpy = spyOn(fakeFeatureFilterProvider, 'getFilterFromConfiguration').and. + returnValue(anyFilter); + service = new FeatureToggleProvider(fakeConfigurationClient, fakeFeatureFilterProvider); + }); + + it('toggles are read using azure configuration client', async () => { + service.getFeatureToggle(featureToggleKey, featureToggleLabel).subscribe((value) => { + + expect(fakeGetConfigurationSetting).toHaveBeenCalledWith( + { key: `.appconfig.featureflag/${featureToggleKey}`, label: featureToggleLabel } + ); + }); + }); + + it('filters are built using the filterProvider', async () => { + service.getFeatureToggle(featureToggleKey, featureToggleLabel).subscribe((value) => { + expect(getFilterConfigurationSpy).toHaveBeenCalled(); + }); + }); + + it('toggle model is built', async () => { + service.getFeatureToggle(featureToggleKey, featureToggleLabel).subscribe((value) => { + expect(value).toEqual(new FeatureToggleModel(anyToggleResponse.id, anyToggleResponse.enabled, [anyFilter])); + }); + }); +}); diff --git a/src/app/modules/shared/feature-toggles/feature-toggle-provider.service.ts b/src/app/modules/shared/feature-toggles/feature-toggle-provider.service.ts new file mode 100644 index 000000000..953cbfe1b --- /dev/null +++ b/src/app/modules/shared/feature-toggles/feature-toggle-provider.service.ts @@ -0,0 +1,40 @@ +import { Inject, Injectable, InjectionToken } from '@angular/core'; +import { AppConfigurationClient } from '@azure/app-configuration'; +import { from, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { AZURE_APP_CONFIGURATION_CONNECTION_STRING } from 'src/environments/environment'; +import { FeatureToggleConfiguration } from './feature-toggle-configuration'; +import { FeatureToggleModel } from './feature-toggle.model'; +import { FeatureFilterProvider } from './filters/feature-filter-provider.service'; + + +const APP_CONFIGURATION_CLIENT = new InjectionToken('Azure configuration client', { + providedIn: 'root', + factory: () => new AppConfigurationClient(AZURE_APP_CONFIGURATION_CONNECTION_STRING) +}); +@Injectable({ + providedIn: 'root', + +}) +export class FeatureToggleProvider { + constructor( + @Inject(APP_CONFIGURATION_CLIENT) + private client: AppConfigurationClient, + private featureFilterProvider: FeatureFilterProvider + ) { } + + public getFeatureToggle(toggleName: string, toggleLabel?: string): Observable { + return from(this.client.getConfigurationSetting({ key: `.appconfig.featureflag/${toggleName}`, label: toggleLabel })).pipe( + map(featureToggleResponse => JSON.parse(featureToggleResponse.value) as FeatureToggleConfiguration), + map(featureToggleConfiguration => { + const filters = featureToggleConfiguration.conditions.client_filters.map(filterConfiguration => + this.featureFilterProvider.getFilterFromConfiguration(filterConfiguration)); + return new FeatureToggleModel( + featureToggleConfiguration.id, + featureToggleConfiguration.enabled, + filters); + } + ) + ); + } +} diff --git a/src/app/modules/shared/feature-toggles/feature-toggle.model.ts b/src/app/modules/shared/feature-toggles/feature-toggle.model.ts new file mode 100644 index 000000000..a33a74ff3 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/feature-toggle.model.ts @@ -0,0 +1,9 @@ +import { FeatureFilterModel } from './filters/feature-filter.model'; + +export class FeatureToggleModel { + constructor( + public readonly name: string, + public readonly enabled: boolean, + public readonly filters: FeatureFilterModel[] + ) { } +} diff --git a/src/app/modules/shared/feature-toggles/filters/feature-filter-configuration.ts b/src/app/modules/shared/feature-toggles/filters/feature-filter-configuration.ts new file mode 100644 index 000000000..1326c138f --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/feature-filter-configuration.ts @@ -0,0 +1,4 @@ +export interface FeatureFilterConfiguration { + name: string; + parameters: any; +} diff --git a/src/app/modules/shared/feature-toggles/filters/feature-filter-provider.service.spec.ts b/src/app/modules/shared/feature-toggles/filters/feature-filter-provider.service.spec.ts new file mode 100644 index 000000000..fb1e21d4f --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/feature-filter-provider.service.spec.ts @@ -0,0 +1,26 @@ +import { AzureAdB2CService } from 'src/app/modules/login/services/azure.ad.b2c.service'; +import { FeatureFilterConfiguration } from './feature-filter-configuration'; +import { FeatureFilterProvider } from './feature-filter-provider.service'; +import { FeatureFilterTypes } from './feature-filter-types'; +import { TargetingFeatureFilterModel } from './targeting/targeting-feature-filter.model'; + + +describe('FeatureFilterProvider', () => { + let fakeUserService: AzureAdB2CService; + let service: FeatureFilterProvider; + let featureFilterConfiguration: FeatureFilterConfiguration; + + beforeEach(() => { + fakeUserService = new AzureAdB2CService(); + spyOn(fakeUserService, 'getUserEmail').and.returnValue('any-user-email'); + spyOn(fakeUserService, 'getUserGroup').and.returnValue('any-user-group'); + service = new FeatureFilterProvider(fakeUserService); + featureFilterConfiguration = { name: FeatureFilterTypes.TARGETING, parameters: {} }; + }); + + it('filter model type is created based on the filter configuration', () => { + const filter = service.getFilterFromConfiguration(featureFilterConfiguration); + + expect(filter.constructor.name).toBe(TargetingFeatureFilterModel.name); + }); +}); diff --git a/src/app/modules/shared/feature-toggles/filters/feature-filter-provider.service.ts b/src/app/modules/shared/feature-toggles/filters/feature-filter-provider.service.ts new file mode 100644 index 000000000..9b4f57bf6 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/feature-filter-provider.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { AzureAdB2CService } from 'src/app/modules/login/services/azure.ad.b2c.service'; +import { FeatureFilterConfiguration } from './feature-filter-configuration'; +import { FeatureFilterTypes } from './feature-filter-types'; +import { FeatureFilterModel } from './feature-filter.model'; +import { TargetingFilterParameters } from './targeting/targeting-feature-filter-parameters'; +import { TargetingFeatureFilterModel } from './targeting/targeting-feature-filter.model'; + + +@Injectable({ + providedIn: 'root', +}) +export class FeatureFilterProvider { + + constructor(private userService: AzureAdB2CService) { } + + getFilterFromConfiguration(featureFilterConfiguration: FeatureFilterConfiguration): FeatureFilterModel { + const featureName = featureFilterConfiguration.name; + switch (featureName) { + case FeatureFilterTypes.TARGETING: { + const appContext = { + username: this.userService.getUserEmail(), + group: this.userService.getUserGroup() + }; + const filter = new TargetingFeatureFilterModel(featureFilterConfiguration.parameters as TargetingFilterParameters, appContext); + return filter; + } + default: { + break; + } + } + } +} diff --git a/src/app/modules/shared/feature-toggles/filters/feature-filter-types.ts b/src/app/modules/shared/feature-toggles/filters/feature-filter-types.ts new file mode 100644 index 000000000..517c617b5 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/feature-filter-types.ts @@ -0,0 +1,3 @@ +export enum FeatureFilterTypes { + TARGETING = 'Microsoft.Targeting' +} diff --git a/src/app/modules/shared/feature-toggles/filters/feature-filter.model.ts b/src/app/modules/shared/feature-toggles/filters/feature-filter.model.ts new file mode 100644 index 000000000..3a0037c89 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/feature-filter.model.ts @@ -0,0 +1,14 @@ + +export interface FeatureFilterModel { + name: string; + parameters: FeatureFilterParameters; + appContext: FeatureFilterAppContext; + evaluate(): boolean; + +} + +// tslint:disable-next-line:no-empty-interface +export interface FeatureFilterParameters { } +// tslint:disable-next-line:no-empty-interface +export interface FeatureFilterAppContext { } + diff --git a/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter-parameters.ts b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter-parameters.ts new file mode 100644 index 000000000..50f624ed1 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter-parameters.ts @@ -0,0 +1,9 @@ +import { FeatureFilterParameters } from '../feature-filter.model'; + +export interface TargetingFilterParameters extends FeatureFilterParameters { + Audience: { + Users: string[]; + Groups: string[]; + }; +} + diff --git a/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter.model.spec.ts b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter.model.spec.ts new file mode 100644 index 000000000..bba414199 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter.model.spec.ts @@ -0,0 +1,38 @@ +import { TargetingFilterParameters } from './targeting-feature-filter-parameters'; +import { TargetingFeatureFilterModel } from './targeting-feature-filter.model'; +import { TargetingFilterAppContext } from './targeting-filter-app-context'; + +describe('TargetingFeatureFilterModel', () => { + + it('targeting feature is true when username matches', () => { + const aUsername = 'user-a'; + const aFilterConfiguration: TargetingFilterParameters = { Audience: { Groups: ['group-x'], Users: [aUsername] } }; + const appContext: TargetingFilterAppContext = { group: 'group-y', username: aUsername }; + const targetingFeatureFilter: TargetingFeatureFilterModel = new TargetingFeatureFilterModel(aFilterConfiguration, appContext); + + const filterEvaluation = targetingFeatureFilter.evaluate(); + + expect(filterEvaluation).toEqual(true); + }); + + it('targeting feature is true when group matches', () => { + const aGroup = 'group-a'; + const aFilterConfiguration: TargetingFilterParameters = { Audience: { Groups: [aGroup], Users: ['user-a'] } }; + const appContext: TargetingFilterAppContext = { group: aGroup, username: 'user-b' }; + const targetingFeatureFilter: TargetingFeatureFilterModel = new TargetingFeatureFilterModel(aFilterConfiguration, appContext); + + const filterEvaluation = targetingFeatureFilter.evaluate(); + + expect(filterEvaluation).toEqual(true); + }); + + it('targeting feature is false when neither group nor username match ', () => { + const aFilterConfiguration: TargetingFilterParameters = { Audience: { Groups: ['group-a'], Users: ['user-a'] } }; + const appContext: TargetingFilterAppContext = { group: 'group-b', username: 'user-b' }; + const targetingFeatureFilter: TargetingFeatureFilterModel = new TargetingFeatureFilterModel(aFilterConfiguration, appContext); + + const filterEvaluation = targetingFeatureFilter.evaluate(); + + expect(filterEvaluation).toEqual(false); + }); +}); diff --git a/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter.model.ts b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter.model.ts new file mode 100644 index 000000000..9f5ed5faf --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-feature-filter.model.ts @@ -0,0 +1,19 @@ +import { FeatureFilterTypes } from '../feature-filter-types'; +import { FeatureFilterModel } from '../feature-filter.model'; +import { TargetingFilterParameters } from './targeting-feature-filter-parameters'; +import { TargetingFilterAppContext } from './targeting-filter-app-context'; + +export class TargetingFeatureFilterModel implements FeatureFilterModel { + + name = FeatureFilterTypes.TARGETING; + constructor(public readonly parameters: TargetingFilterParameters, public readonly appContext: TargetingFilterAppContext) { + } + + evaluate(): boolean { + const userCoincidence = this.parameters.Audience.Users.includes(this.appContext.username); + const groupCoincidence = this.parameters.Audience.Groups.includes(this.appContext.group); + return userCoincidence || groupCoincidence; + } +} + + diff --git a/src/app/modules/shared/feature-toggles/filters/targeting/targeting-filter-app-context.ts b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-filter-app-context.ts new file mode 100644 index 000000000..05d66efb8 --- /dev/null +++ b/src/app/modules/shared/feature-toggles/filters/targeting/targeting-filter-app-context.ts @@ -0,0 +1,9 @@ +import { FeatureFilterAppContext } from '../feature-filter.model'; + +export interface TargetingFilterAppContext extends FeatureFilterAppContext { + username; + group; +} + + +