From 8b5f018a6404d1ac8e08dd931ca2797c4320ce48 Mon Sep 17 00:00:00 2001 From: Divyanshi Gupta Date: Mon, 14 Jan 2019 20:33:04 +0530 Subject: [PATCH] feat(warning-page): Implement new design and action on opt-in button click (#22) * fix(feature-warning): add new design for warning-page * fix(feature-warning): add service for enabling feature * fix(feature-warning): add feature enable action on opt-in button click * fix(feature-toggle): add option to display default/customised warning * fix(feature-toggle): fix tests * fix(feature-toggle): add option to display default/customised warning * fix(feature-warning): add new design for warning page * fix(demo-app): up and running demo app with new changes * fix(demo-app): fix feature toggle mock service --- .prettierrc | 6 + .../feature-toggle.component.html | 14 ++ .../feature-toggle.component.spec.ts | 104 +++++++++----- .../feature-toggle.component.ts | 49 +++++-- .../service/enable-feature.service.spec.ts | 128 ++++++++++++++++++ .../src/lib/service/enable-feature.service.ts | 80 +++++++++++ .../feature-warning-page.component.html | 68 +++------- .../feature-warning-page.component.less | 53 +++----- .../feature-warning-page.component.ts | 100 ++++++++++++-- projects/ngx-feature-flag/src/public_api.ts | 8 +- src/app/app.module.ts | 42 ++++-- ...feature-toggle-loader.example.component.ts | 53 ++++---- .../feature-toggle-loader.example.module.ts | 12 +- ...eature-toggle-service.example.component.ts | 36 +++-- .../feature-toggle-service.example.module.ts | 5 +- .../feature-toggle.example.component.html | 121 +++++++++++++---- .../feature-toggle.example.component.ts | 53 ++++---- .../feature-toggle.example.module.ts | 5 +- .../feature-toggle.component.spec.ts | 121 ++++++++++++----- .../service/enable-feature-mock.service.ts | 36 +++++ .../service/feature-toggles-mock.service.ts | 31 +++-- src/app/service/notifications-mock.service.ts | 8 ++ 22 files changed, 819 insertions(+), 314 deletions(-) create mode 100644 .prettierrc create mode 100644 projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.html create mode 100644 projects/ngx-feature-flag/src/lib/service/enable-feature.service.spec.ts create mode 100644 projects/ngx-feature-flag/src/lib/service/enable-feature.service.ts create mode 100644 src/app/service/enable-feature-mock.service.ts create mode 100644 src/app/service/notifications-mock.service.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a7e9225 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "arrowParens": "always", + "singleQuote": true, + "bracketSpacing": true, + "printWidth": 100 +} diff --git a/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.html b/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.html new file mode 100644 index 0000000..a9379ae --- /dev/null +++ b/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.html @@ -0,0 +1,14 @@ + + + + + + + diff --git a/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.spec.ts b/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.spec.ts index 44159ec..3ecc678 100644 --- a/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.spec.ts +++ b/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.spec.ts @@ -1,17 +1,46 @@ import { HttpClientModule } from '@angular/common/http'; -import { Component } from '@angular/core'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { of } from 'rxjs'; import { Feature } from '../models/feature'; import { FeatureTogglesService } from '../service/feature-toggles.service'; import { FeatureToggleComponent } from './feature-toggle.component'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: `f8-host-component`, + template: ` + +
My content here
+ ` +}) +class TestHostComponent1 {} + +@Component({ + selector: `f8-host-component`, + template: ` + + +
My content here
+ ` +}) +class TestHostComponent2 {} + +@Component({ + selector: `f8-feature-warning-page`, + template: ` +
Warning Page!
+ ` +}) +class TestWarningComponent {} describe('FeatureToggleComponent', () => { - let featureServiceMock: jasmine.SpyObj; - let hostFixture: ComponentFixture; + let mockFeatureService: jasmine.SpyObj; + let hostFixture1: ComponentFixture; + let hostFixture2: ComponentFixture; - const feature: Feature = { + const feature1: Feature = { attributes: { name: 'Planner', description: 'Description', @@ -22,48 +51,57 @@ describe('FeatureToggleComponent', () => { id: 'Planner' }; - @Component({ - selector: `f8-host-component`, - template: `
My content here
` - }) - class TestHostComponent { - } + const feature2: Feature = { + attributes: { + name: 'Planner Query', + description: 'Description', + enabled: true, + 'enablement-level': 'internal', + 'user-enabled': false + }, + id: 'PlannerQuery' + }; + beforeEach(() => { - featureServiceMock = jasmine.createSpyObj('FeatureTogglesService', ['isFeatureUserEnabled']); + mockFeatureService = jasmine.createSpyObj('FeatureTogglesService', ['getFeature']); TestBed.configureTestingModule({ - imports: [FormsModule, HttpClientModule], - declarations: [FeatureToggleComponent, TestHostComponent], - providers: [ - { - provide: FeatureTogglesService, useValue: featureServiceMock - } - ] + imports: [FormsModule, HttpClientModule, RouterModule], + declarations: [ + FeatureToggleComponent, + TestHostComponent1, + TestHostComponent2, + TestWarningComponent + ], + providers: [{ provide: FeatureTogglesService, useValue: mockFeatureService }], + schemas: [NO_ERRORS_SCHEMA] }); - hostFixture = TestBed.createComponent(TestHostComponent); + hostFixture1 = TestBed.createComponent(TestHostComponent1); + hostFixture2 = TestBed.createComponent(TestHostComponent2); }); it('should render content if feature is user enabled', async(() => { - // given - - featureServiceMock.isFeatureUserEnabled.and.returnValue(of(true)); - hostFixture.detectChanges(); - hostFixture.whenStable().then(() => { - expect(hostFixture.nativeElement.querySelector('div').innerText).toEqual('My content here'); + mockFeatureService.getFeature.and.returnValue(of(feature1)); + hostFixture1.detectChanges(); + hostFixture1.whenStable().then(() => { + expect(hostFixture1.nativeElement.querySelector('div').innerText).toEqual('My content here'); }); })); it('should not render content if feature is not user enabled', async(() => { - // given - featureServiceMock.isFeatureUserEnabled.and.returnValue(of(false)); + mockFeatureService.getFeature.and.returnValue(of(feature2)); + hostFixture1.detectChanges(); + hostFixture1.whenStable().then(() => { + expect(hostFixture1.nativeElement.querySelector('div')).toBeNull(); + }); + })); - // given - feature.attributes.enabled = false; - feature.attributes['user-enabled'] = true; - hostFixture.detectChanges(); - hostFixture.whenStable().then(() => { - expect(hostFixture.nativeElement.querySelector('div')).toBeNull(); + it('should render default warning template when showFeatureOptIn is true', async(() => { + mockFeatureService.getFeature.and.returnValue(of(feature2)); + hostFixture2.detectChanges(); + hostFixture2.whenStable().then(() => { + expect(hostFixture2.nativeElement.querySelector('div').innerText).toEqual('Warning Page!'); }); })); }); diff --git a/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.ts b/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.ts index 0f6ff20..9bf9a21 100644 --- a/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.ts +++ b/projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component.ts @@ -1,23 +1,26 @@ -import { - Component, - Input, - OnInit, - TemplateRef -} from '@angular/core'; -import { Observable } from 'rxjs'; +import { Component, Input, OnInit, TemplateRef } from '@angular/core'; +import { Observable, of } from 'rxjs'; import { FeatureTogglesService } from '../service/feature-toggles.service'; +import { Feature } from '../models/feature'; +import { map, catchError, tap } from 'rxjs/operators'; + +interface FeatureEnablementData { + enabled: boolean; + level: string; +} @Component({ selector: 'f8-feature-toggle', - template: `` + templateUrl: './feature-toggle.component.html' }) export class FeatureToggleComponent implements OnInit { - @Input() featureName: string; @Input() userLevel: TemplateRef; @Input() defaultLevel: TemplateRef; + @Input() showFeatureOptIn: boolean = false; - enabled: Observable<{} | boolean>; + isFeatureUserEnabled: boolean = false; + feature$: Observable<{} | FeatureEnablementData>; constructor(private featureService: FeatureTogglesService) {} @@ -25,8 +28,30 @@ export class FeatureToggleComponent implements OnInit { if (!this.featureName) { throw new Error('Attribute `featureName` should not be null or empty'); } - - this.enabled = this.featureService.isFeatureUserEnabled(this.featureName); + this.feature$ = this.featureService.getFeature(this.featureName).pipe( + map((feature: Feature) => { + if (feature.attributes) { + let featureEnablementData: FeatureEnablementData = { + enabled: feature.attributes.enabled && feature.attributes['user-enabled'], + level: feature.attributes['enablement-level'] + }; + return featureEnablementData; + } else { + return {}; + } + }), + tap((feature: FeatureEnablementData) => { + if (feature && feature.enabled) { + this.isFeatureUserEnabled = true; + } else { + this.isFeatureUserEnabled = false; + } + }), + catchError(() => of({})) + ); } + setUserLevelTemplate() { + this.isFeatureUserEnabled = true; + } } diff --git a/projects/ngx-feature-flag/src/lib/service/enable-feature.service.spec.ts b/projects/ngx-feature-flag/src/lib/service/enable-feature.service.spec.ts new file mode 100644 index 0000000..17dc13d --- /dev/null +++ b/projects/ngx-feature-flag/src/lib/service/enable-feature.service.spec.ts @@ -0,0 +1,128 @@ +import { + HttpClientTestingModule, + HttpTestingController, + TestRequest +} from '@angular/common/http/testing'; +import { async, getTestBed, TestBed } from '@angular/core/testing'; +import { AuthenticationService } from 'ngx-login-client'; +import { first } from 'rxjs/operators'; +import { FABRIC8_FEATURE_TOGGLES_API_URL } from './feature-toggles.service'; +import { EnableFeatureService, ExtProfile, ExtUser } from './enable-feature.service'; +import { Logger } from 'ngx-base'; +import { UserService } from 'ngx-login-client'; + +describe('EnableFeature service:', () => { + let mockAuthService: jasmine.SpyObj; + let mockUserService: jasmine.SpyObj; + let enableFeatureService: EnableFeatureService; + let httpTestingController: HttpTestingController; + + let url: string; + + beforeEach(() => { + mockAuthService = jasmine.createSpyObj('AuthenticationService', ['getToken']); + mockAuthService.getToken.and.returnValue('mock-auth-token'); + mockUserService = jasmine.createSpyObj('UserService', ['loggedInUser']); + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { + provide: AuthenticationService, + useValue: mockAuthService + }, + { + provide: FABRIC8_FEATURE_TOGGLES_API_URL, + useValue: 'http://example.com/api/' + }, + { + provide: UserService, + useValue: mockUserService + }, + EnableFeatureService, + Logger + ] + }); + enableFeatureService = getTestBed().get(EnableFeatureService); + httpTestingController = getTestBed().get(HttpTestingController); + + url = getTestBed().get(FABRIC8_FEATURE_TOGGLES_API_URL); + }); + + afterEach(() => { + httpTestingController.verify(); + }); + + it('should be instantiable', async((): void => { + expect(enableFeatureService).toBeDefined(); + })); + + describe('#getUpdate', () => { + it('should send a PATCH', (done: DoneFn): void => { + enableFeatureService + .update({} as ExtProfile) + .pipe(first()) + .subscribe( + (): void => { + done(); + } + ); + const req: TestRequest = httpTestingController.expectOne('http://example.com/api/users'); + expect(req.request.method).toEqual('PATCH'); + req.flush({}); + }); + + it('should send correct headers', (done: DoneFn): void => { + enableFeatureService + .update({} as ExtProfile) + .pipe(first()) + .subscribe( + (): void => { + done(); + } + ); + const req: TestRequest = httpTestingController.expectOne('http://example.com/api/users'); + expect(req.request.headers.get('Authorization')).toEqual('Bearer mock-auth-token'); + req.flush({}); + }); + + it('should send correct payload', (done: DoneFn): void => { + const attributes: ExtProfile = { featureLevel: 'beta' } as ExtProfile; + enableFeatureService + .update(attributes) + .pipe(first()) + .subscribe( + (): void => { + done(); + } + ); + const req: TestRequest = httpTestingController.expectOne('http://example.com/api/users'); + expect(req.request.body).toEqual( + JSON.stringify({ + data: { + attributes, + type: 'identities' + } + }) + ); + req.flush({}); + }); + + it('should return expected data', (done: DoneFn): void => { + const data: ExtUser = { + attributes: { + featureLevel: 'beta' + } + } as ExtUser; + enableFeatureService + .update(data.attributes) + .pipe(first()) + .subscribe( + (user: ExtUser): void => { + expect(user).toEqual(data); + done(); + } + ); + httpTestingController.expectOne('http://example.com/api/users').flush({ data }); + }); + }); +}); diff --git a/projects/ngx-feature-flag/src/lib/service/enable-feature.service.ts b/projects/ngx-feature-flag/src/lib/service/enable-feature.service.ts new file mode 100644 index 0000000..d5cdbd1 --- /dev/null +++ b/projects/ngx-feature-flag/src/lib/service/enable-feature.service.ts @@ -0,0 +1,80 @@ +import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { cloneDeep } from 'lodash'; +import { Logger } from 'ngx-base'; +import { AuthenticationService, Profile, User, UserService } from 'ngx-login-client'; +import { ConnectableObservable, Observable, throwError as observableThrowError } from 'rxjs'; +import { catchError, map, publish } from 'rxjs/operators'; +import { FABRIC8_FEATURE_TOGGLES_API_URL } from './feature-toggles.service'; + +interface ExtUserResponse { + data: ExtUser; +} + +export class ExtUser extends User { + attributes: ExtProfile; +} + +export class ExtProfile extends Profile { + contextInformation: any; + registrationCompleted: boolean; + featureLevel: string; +} + +@Injectable() +export class EnableFeatureService { + private headers = new HttpHeaders({ 'Content-Type': 'application/json' }); + private usersUrl: string; + + constructor( + protected auth: AuthenticationService, + protected http: HttpClient, + protected logger: Logger, + protected userService: UserService, + @Inject(FABRIC8_FEATURE_TOGGLES_API_URL) apiUrl: string + ) { + if (this.auth.getToken() != undefined) { + this.headers = this.headers.set('Authorization', 'Bearer ' + this.auth.getToken()); + } + this.usersUrl = apiUrl + 'users'; + } + + createTransientProfile(): ExtProfile { + let profile: ExtUser; + + (this.userService.loggedInUser.pipe( + map( + (user: User): void => { + profile = cloneDeep(user) as ExtUser; + if (profile.attributes !== undefined) { + profile.attributes.contextInformation = + (user as ExtUser).attributes.contextInformation || {}; + } + } + ), + publish() + ) as ConnectableObservable).connect(); + + return profile !== undefined && profile.attributes !== undefined + ? profile.attributes + : ({} as ExtProfile); + } + + update(profile: ExtProfile): Observable { + const payload: any = JSON.stringify({ + data: { + attributes: profile, + type: 'identities' + } + }); + return this.http.patch(this.usersUrl, payload, { headers: this.headers }).pipe( + map((response: ExtUserResponse): ExtUser => response.data), + catchError((error: HttpErrorResponse): Observable => this.handleError(error)) + ); + } + + protected handleError(error: HttpErrorResponse): Observable { + this.logger.error(error); + return observableThrowError(error.message || error); + } +} diff --git a/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.html b/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.html index fd57924..74c2704 100644 --- a/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.html +++ b/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.html @@ -1,50 +1,24 @@ -
-
-
-
-
- -

Internal Features Opt-in

-

These features are only available to Red Hat users and have no guarantee of performance or stability. - Use these at your own risk.

- -
-
-
-
-
-
-
-
- -

Experimental Features Opt-in

-

These features are currently in beta testing and have no guarantee of performance or stability. - Use these at your own risk.

- -
-
-
-
-
-
-
-
- Β -

Beta Features Opt-in

-

These features are currently in beta testing and have no guarantee of performance or stability. - Use these at your own risk.

- -
-
-
-
-
-
-
-
- -

Ooops

-

This feature has been disabled by your admin page.

+
+
+
+
+
+
+

{{ featureWarning.title }} Feature Opt-in

+
+
{{ featureWarning.description }}
+
+ +
+
+
+
diff --git a/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.less b/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.less index ef95ceb..9515ab0 100644 --- a/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.less +++ b/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.less @@ -1,43 +1,28 @@ @import (reference) '../../assets/stylesheets/shared/main.less'; -.feature-level(@color-for-feature) { - &-page { - height: 100vh; - color: @color-pf-black-100; - background-color: @color-pf-black-800; - text-align: center; - .feature-content { - margin-top: 5%; - i { - color: @color-for-feature; - } - .btn-feature-level { - &:extend(.btn-default); - color: @color-pf-black-100; - border-color: @color-for-feature; - background-color: @color-for-feature; - background-image: linear-gradient(to bottom, @color-for-feature 0, @color-for-feature 100%); - &:hover { - background-color: @color-for-feature; - background-image: none; - border-color: darken((@color-for-feature), 10%); - } - } - } - } +.warning-page-container { + margin-top: 40%; } -// EXPERIMENTAL -.experimental { - .feature-level(@color-experimental); +.title-text { + text-align: center; + padding: 10px; + font-size: 2em; } -// BETA -.beta { - .feature-level(@color-beta); +.link-text { + text-align: center; + margin-top: 10px; + margin-bottom: 20px; + cursor: pointer; + font-size: 0.9em; } -// INTERNAL -.internal { - .feature-level(@color-internal); +.card-body { + text-align: center; +} + +.opt-in-button { + margin-top: 30px; + margin-bottom: 20px; } diff --git a/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.ts b/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.ts index 6d227fe..050f753 100644 --- a/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.ts +++ b/projects/ngx-feature-flag/src/lib/warning-page/feature-warning-page.component.ts @@ -1,12 +1,15 @@ -import { - Component, - Input, - OnDestroy, - OnInit -} from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, Output, EventEmitter } from '@angular/core'; import { AuthenticationService, UserService } from 'ngx-login-client'; import { Subscription } from 'rxjs'; +import { EnableFeatureService, ExtProfile, ExtUser } from '../service/enable-feature.service'; +import { first } from 'rxjs/operators'; +import { Notifications, NotificationType } from 'ngx-base'; + +export interface featureWarningData { + title: string; + description: string; +} @Component({ selector: 'f8-feature-warning-page', @@ -14,16 +17,49 @@ import { Subscription } from 'rxjs'; styleUrls: ['./feature-warning-page.component.less'] }) export class FeatureWarningPageComponent implements OnInit, OnDestroy { - - enableFeatures: boolean; @Input() level: string; + + @Output() readonly onOptInButtonClick: EventEmitter = new EventEmitter(); + + featureWarning: featureWarningData; profileSettingsLink: string; private userSubscription: Subscription; - constructor(private userService: UserService, - private authService: AuthenticationService) { - if (authService.isLoggedIn()) { - this.userSubscription = userService.loggedInUser.subscribe(val => { + private featureWarningDataMap: Map = new Map([ + [ + 'internal', + { + title: 'Internal', + description: + 'These features are only available to Red Hat users and have no guarantee of performance or stability. Use these at your own risk.' + } + ], + [ + 'experimental', + { + title: 'Experminetal', + description: + 'These features are currently in experimental testing and have no guarantee of performance or stability. Use these at your own risk.' + } + ], + [ + 'beta', + { + title: 'Beta', + description: + 'These features are currently in beta testing and have no guarantee of performance or stability. Use these at your own risk.' + } + ] + ]); + + constructor( + private userService: UserService, + private authService: AuthenticationService, + private enableFeatureService: EnableFeatureService, + private notifications: Notifications + ) { + if (this.authService.isLoggedIn()) { + this.userSubscription = userService.loggedInUser.subscribe((val) => { if (val.id) { this.profileSettingsLink = '/' + val.attributes.username + '/_settings/feature-opt-in'; } @@ -34,7 +70,7 @@ export class FeatureWarningPageComponent implements OnInit, OnDestroy { } ngOnInit() { - this.enableFeatures = false; + this.featureWarning = this.featureWarningDataMap.get(this.level); } ngOnDestroy() { @@ -43,4 +79,42 @@ export class FeatureWarningPageComponent implements OnInit, OnDestroy { } } + enableFeature() { + const profile: ExtProfile = this.getTransientProfile(); + this.enableFeatureService + .update(profile) + .pipe(first()) + .subscribe( + (user: ExtUser): void => { + this.userService.currentLoggedInUser = user; + this.onOptInButtonClick.emit(); + this.notifications.message({ + message: this.featureWarning.title + ` feature level enabled`, + type: NotificationType.SUCCESS + }); + }, + () => { + this.notifications.message({ + message: 'Failed to enable ' + this.featureWarning.title + ' feature level', + type: NotificationType.DANGER + }); + } + ); + } + + getTransientProfile(): ExtProfile { + const profile: ExtProfile = this.enableFeatureService.createTransientProfile(); + if (profile) { + if (!profile['contextInformation']) { + profile['contextInformation'] = {}; + } + if (this.level) { + profile['featureLevel'] = this.level; + } + // Delete extra information that make the update fail if present + delete profile['username']; + delete profile['registrationCompleted']; + } + return profile; + } } diff --git a/projects/ngx-feature-flag/src/public_api.ts b/projects/ngx-feature-flag/src/public_api.ts index 973c414..61f1ce3 100644 --- a/projects/ngx-feature-flag/src/public_api.ts +++ b/projects/ngx-feature-flag/src/public_api.ts @@ -7,11 +7,15 @@ export { FeatureFlagHomeComponent } from './lib/home/feature-flag-home.component export { FeatureContainerComponent } from './lib/feature-loader/feature-loader.component'; export { FeatureToggleComponent } from './lib/feature-wrapper/feature-toggle.component'; -export { FeatureTogglesService, FABRIC8_FEATURE_TOGGLES_API_URL } from './lib/service/feature-toggles.service'; +export { + FeatureTogglesService, + FABRIC8_FEATURE_TOGGLES_API_URL +} from './lib/service/feature-toggles.service'; +export { EnableFeatureService } from './lib/service/enable-feature.service'; +export { FeatureFlagMapping } from './lib/feature-flag.mapping'; export { Feature } from './lib/models/feature'; export { FeatureFlagConfig } from './lib/models/feature-flag-config'; export { FeatureFlagResolver } from './lib/resolver/feature-flag.resolver'; export { FeatureFlagModule } from './lib/feature-flag.module'; - diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 602bcc2..6e83486 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,11 +4,16 @@ import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { Broadcaster, Logger } from 'ngx-base'; -import { AUTH_API_URL, AuthenticationService, REALM, SSO_API_URL, UserService } from 'ngx-login-client'; -import { FeatureFlagModule } from '../../projects/ngx-feature-flag/src/lib/feature-flag.module'; -import { FABRIC8_FEATURE_TOGGLES_API_URL, FeatureTogglesService } - from '../../projects/ngx-feature-flag/src/lib/service/feature-toggles.service'; +import { Broadcaster, Logger, Notifications } from 'ngx-base'; +import { + AUTH_API_URL, + AuthenticationService, + REALM, + SSO_API_URL, + UserService +} from 'ngx-login-client'; +import { FeatureFlagModule } from 'ngx-feature-flag'; +import { FABRIC8_FEATURE_TOGGLES_API_URL, FeatureTogglesService } from 'ngx-feature-flag'; import { AppRoutingModule } from './app-routing.module'; // App components import { AppComponent } from './app.component'; @@ -19,6 +24,10 @@ import { NavbarModule } from './navbar/navbar.module'; import { FeatureTogglesServiceMock } from './service/feature-toggles-mock.service'; import { DemoComponentsModule } from './welcome/demo-components.module'; import { WelcomeComponent } from './welcome/welcome.component'; +import { EnableFeatureService } from 'ngx-feature-flag'; +import { EnableFeatureMockService } from './service/enable-feature-mock.service'; +import { NotificationsMockService } from './service/notifications-mock.service'; + @NgModule({ imports: [ AppRoutingModule, @@ -34,22 +43,25 @@ import { WelcomeComponent } from './welcome/welcome.component'; FeatureToggleLoaderExampleModule, FeatureToggleServiceExampleModule ], - declarations: [ - AppComponent, - WelcomeComponent - ], + declarations: [AppComponent, WelcomeComponent], providers: [ - {provide: AUTH_API_URL, useValue: 'https://auth.prod-preview.openshift.io/api/'}, - {provide: SSO_API_URL, useValue: 'https://sso.prod-preview.openshift.io/api/'}, - {provide: REALM, useValue: ''}, - {provide: FABRIC8_FEATURE_TOGGLES_API_URL, useValue: 'https://api.prod-preview.openshift.io/api/'}, + { provide: AUTH_API_URL, useValue: 'https://auth.prod-preview.openshift.io/api/' }, + { provide: SSO_API_URL, useValue: 'https://sso.prod-preview.openshift.io/api/' }, + { provide: REALM, useValue: '' }, + { + provide: FABRIC8_FEATURE_TOGGLES_API_URL, + useValue: 'https://api.prod-preview.openshift.io/api/' + }, Broadcaster, AuthenticationService, UserService, Logger, + Notifications, // FeatureTogglesService //uncomment if you want to use prod-preview service - {provide: FeatureTogglesService, useClass: FeatureTogglesServiceMock} + { provide: FeatureTogglesService, useClass: FeatureTogglesServiceMock }, + { provide: EnableFeatureService, useClass: EnableFeatureMockService }, + { provide: Notifications, useClass: NotificationsMockService } ], bootstrap: [AppComponent] }) -export class AppModule { } +export class AppModule {} diff --git a/src/app/feature-toggle-loader/feature-toggle-loader.example.component.ts b/src/app/feature-toggle-loader/feature-toggle-loader.example.component.ts index 480d5a9..4afbe29 100644 --- a/src/app/feature-toggle-loader/feature-toggle-loader.example.component.ts +++ b/src/app/feature-toggle-loader/feature-toggle-loader.example.component.ts @@ -1,26 +1,23 @@ -import { - ChangeDetectorRef, - Component, - OnInit, ViewChild -} from '@angular/core'; - -import { FeatureContainerComponent } from '../../../projects/ngx-feature-flag/src/lib/feature-loader/feature-loader.component'; -import { FeatureTogglesService } from '../../../projects/ngx-feature-flag/src/lib/service/feature-toggles.service'; +import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; +import { FeatureContainerComponent } from 'ngx-feature-flag'; +import { FeatureTogglesService } from 'ngx-feature-flag'; @Component({ selector: 'app-feature-toggle-loader-example', - styles: [` - .sample-form .form-horizontal .form-group { - margin-left: 0px; - } - .padding-top-15 { - padding-top: 15px; - } - .padding-bottom-15 { - padding-bottom: 15px; - } - `], + styles: [ + ` + .sample-form .form-horizontal .form-group { + margin-left: 0px; + } + .padding-top-15 { + padding-top: 15px; + } + .padding-bottom-15 { + padding-bottom: 15px; + } + ` + ], templateUrl: './feature-toggle-loader.example.component.html' }) export class FeatureToggleLoaderExampleComponent implements OnInit { @@ -32,8 +29,7 @@ export class FeatureToggleLoaderExampleComponent implements OnInit { featureFlagEnablementLevel: string = 'beta'; userLevel: string = 'internal'; - constructor(private featureToggleService: FeatureTogglesService, private cd: ChangeDetectorRef) { - } + constructor(private featureToggleService: FeatureTogglesService, private cd: ChangeDetectorRef) {} ngOnInit(): void { (this.featureToggleService as any).featureFlagName = this.featureFlagName; @@ -46,16 +42,21 @@ export class FeatureToggleLoaderExampleComponent implements OnInit { if ((this.featureToggleService as any).featureFlagName === undefined) { return; } - if (this.featureFlagEnablementLevel !== 'internal' && + if ( + this.featureFlagEnablementLevel !== 'internal' && this.featureFlagEnablementLevel !== 'experimental' && this.featureFlagEnablementLevel !== 'beta' && - this.featureFlagEnablementLevel !== 'released') { - this.featureFlagEnablementLevel = (this.featureToggleService as any).featureFlagEnablementLevel; // keep previous valid version + this.featureFlagEnablementLevel !== 'released' + ) { + this.featureFlagEnablementLevel = (this + .featureToggleService as any).featureFlagEnablementLevel; // keep previous valid version } - if (this.userLevel !== 'internal' && + if ( + this.userLevel !== 'internal' && this.userLevel !== 'experimental' && this.userLevel !== 'beta' && - this.userLevel !== 'released') { + this.userLevel !== 'released' + ) { this.userLevel = (this.featureToggleService as any).userLevel; // keep previous valid version } (this.featureToggleService as any).featureFlagName = this.featureFlagName; diff --git a/src/app/feature-toggle-loader/feature-toggle-loader.example.module.ts b/src/app/feature-toggle-loader/feature-toggle-loader.example.module.ts index f74c5a7..8397ff9 100644 --- a/src/app/feature-toggle-loader/feature-toggle-loader.example.module.ts +++ b/src/app/feature-toggle-loader/feature-toggle-loader.example.module.ts @@ -3,14 +3,13 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { TabsetConfig, TabsModule } from 'ngx-bootstrap/tabs'; -import { FeatureFlagModule } from '../../../projects/ngx-feature-flag/src/lib/feature-flag.module'; +import { FeatureFlagModule } from 'ngx-feature-flag'; import { DemoComponentsModule } from '../welcome/demo-components.module'; import { DynamicallyLoadedComponent } from './dynamically-loaded.component'; import { FeatureToggleLoaderExampleComponent } from './feature-toggle-loader.example.component'; - -import { FeatureFlagMapping } from '../../../projects/ngx-feature-flag/src/lib/feature-flag.mapping'; -import { FeatureContainerComponent } from '../../../projects/ngx-feature-flag/src/lib/feature-loader/feature-loader.component'; +import { FeatureFlagMapping } from 'ngx-feature-flag'; +import { FeatureContainerComponent } from 'ngx-feature-flag'; import { MyFeatureFlagMapping } from './feature-flag.mapping'; @NgModule({ @@ -23,10 +22,7 @@ import { MyFeatureFlagMapping } from './feature-flag.mapping'; ], declarations: [FeatureToggleLoaderExampleComponent, DynamicallyLoadedComponent], exports: [FeatureToggleLoaderExampleComponent, DynamicallyLoadedComponent], - providers: [ - TabsetConfig, - {provide: FeatureFlagMapping, useClass: MyFeatureFlagMapping} - ], + providers: [TabsetConfig, { provide: FeatureFlagMapping, useClass: MyFeatureFlagMapping }], entryComponents: [DynamicallyLoadedComponent, FeatureContainerComponent] }) export class FeatureToggleLoaderExampleModule { diff --git a/src/app/feature-toggle-service/feature-toggle-service.example.component.ts b/src/app/feature-toggle-service/feature-toggle-service.example.component.ts index 7fdb5e2..ab9e5f8 100644 --- a/src/app/feature-toggle-service/feature-toggle-service.example.component.ts +++ b/src/app/feature-toggle-service/feature-toggle-service.example.component.ts @@ -1,39 +1,37 @@ -import { - Component -} from '@angular/core'; - -import { FeatureTogglesService } from '../../../projects/ngx-feature-flag/src/lib/service/feature-toggles.service'; +import { Component } from '@angular/core'; +import { FeatureTogglesService } from 'ngx-feature-flag'; @Component({ selector: 'app-feature-toggle-example', - styles: [` - .sample-form .form-horizontal .form-group { - margin-left: 0px; - } - .padding-top-15 { - padding-top: 15px; - } - .padding-bottom-15 { - padding-bottom: 15px; - } - `], + styles: [ + ` + .sample-form .form-horizontal .form-group { + margin-left: 0px; + } + .padding-top-15 { + padding-top: 15px; + } + .padding-bottom-15 { + padding-bottom: 15px; + } + ` + ], templateUrl: './feature-toggle-service.example.component.html', providers: [FeatureTogglesService] }) export class FeatureToggleServiceExampleComponent { - getAllFeaturesOutput: string; constructor(private featureToggleService: FeatureTogglesService) {} getAllFeaturesEnabledByLevel() { - this.featureToggleService.getAllFeaturesEnabledByLevel().subscribe(val => { + this.featureToggleService.getAllFeaturesEnabledByLevel().subscribe((val) => { console.log(JSON.stringify(val)); this.getAllFeaturesOutput = JSON.stringify(val, null, 2); }); } getAllFeaturesEnabledByIds() { - this.featureToggleService.getFeatures(['Test']).subscribe(val => { + this.featureToggleService.getFeatures(['Test']).subscribe((val) => { console.log(JSON.stringify(val)); this.getAllFeaturesOutput = JSON.stringify(val, null, 2); }); diff --git a/src/app/feature-toggle-service/feature-toggle-service.example.module.ts b/src/app/feature-toggle-service/feature-toggle-service.example.module.ts index 511df81..ff01caa 100644 --- a/src/app/feature-toggle-service/feature-toggle-service.example.module.ts +++ b/src/app/feature-toggle-service/feature-toggle-service.example.module.ts @@ -3,12 +3,11 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { TabsetConfig, TabsModule } from 'ngx-bootstrap/tabs'; -import { FeatureFlagModule } from '../../../projects/ngx-feature-flag/src/lib/feature-flag.module'; -import { FeatureTogglesService } from '../../../projects/ngx-feature-flag/src/lib/service/feature-toggles.service'; +import { FeatureFlagModule } from 'ngx-feature-flag'; +import { FeatureTogglesService } from 'ngx-feature-flag'; import { DemoComponentsModule } from '../welcome/demo-components.module'; import { FeatureToggleServiceExampleComponent } from './feature-toggle-service.example.component'; - @NgModule({ imports: [ CommonModule, diff --git a/src/app/feature-toggle/feature-toggle.example.component.html b/src/app/feature-toggle/feature-toggle.example.component.html index 45cf5cf..78f8c43 100644 --- a/src/app/feature-toggle/feature-toggle.example.component.html +++ b/src/app/feature-toggle/feature-toggle.example.component.html @@ -2,53 +2,73 @@

f8-feature-toggle example

-
+
-
With `f8-feature-toggle`, you can activate a feature depending on the level your user consent. It uses - `enableByLevel` strategy. It allows to tag a feature for a particular level. Feature flag level are: internal, experimental, beta, released. +
+ With `f8-feature-toggle`, you can activate a feature depending on the level your user + consent. It uses `enableByLevel` strategy. It allows to tag a feature for a particular + level. Feature flag level are: internal, experimental, beta, + released.
    -
  • internal will only show it to accounts with an @redhat.com email. It will have a notification on the screen: - "This feature is open to Red Hat users only. You can manage pre-production features on your profile page."
  • -
  • experimental will be "This feature is experimental. You can manage pre-production features on your profile page."
  • -
  • beta notification will be "This feature is in beta. You can manage pre-production features on your profile page."
  • +
  • + internal will only show it to accounts with an @redhat.com email. It + will have a notification on the screen: "This feature is open to Red Hat users only. + You can manage pre-production features on your profile page." +
  • +
  • + experimental will be "This feature is experimental. You can manage + pre-production features on your profile page." +
  • +
  • + beta notification will be "This feature is in beta. You can manage + pre-production features on your profile page." +
  • released makes the feature default for everyone.
-

NOTE: Anonymous user can only see the `released` state. For other level, you need to be logged-in.

- - +

+ NOTE: Anonymous user can only see the `released` state. For other level, you need to be + logged-in. +

- +
-

- 😀 ðŸĪŠ -

+

😀 ðŸĪŠ

Yay activated just for you

-

- ðŸ˜Ē 😭 -

+

ðŸ˜Ē 😭

Not activated for your user!

-
-

+

Settings

-
-
To test the component you can play with mock value. Valid data for userLevel and FeatureFlagEnablementLevel are internal, experimental, beta, released
-
To test with prod-preview in AppModule, use FeatureTogglesService insetead of FeatureTogglesServiceMock
+
+
+ To test the component you can play with mock value. Valid data for userLevel and + FeatureFlagEnablementLevel are internal, experimental, beta, + released +
+
+ To test with prod-preview in AppModule, use FeatureTogglesService insetead of + FeatureTogglesServiceMock +
@@ -56,19 +76,56 @@

Settings

- +
- - + +
- + +
+
+ +
- +
@@ -76,16 +133,20 @@

Settings

Code

-
+
- + - +
diff --git a/src/app/feature-toggle/feature-toggle.example.component.ts b/src/app/feature-toggle/feature-toggle.example.component.ts index 70586f1..ab71c82 100644 --- a/src/app/feature-toggle/feature-toggle.example.component.ts +++ b/src/app/feature-toggle/feature-toggle.example.component.ts @@ -1,25 +1,23 @@ -import { - ChangeDetectorRef, - Component, - OnInit, ViewChild -} from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; -import { FeatureToggleComponent } from '../../../projects/ngx-feature-flag/src/lib/feature-wrapper/feature-toggle.component'; -import { FeatureTogglesService } from '../../../projects/ngx-feature-flag/src/lib/service/feature-toggles.service'; +import { FeatureToggleComponent } from 'ngx-feature-flag'; +import { FeatureTogglesService } from 'ngx-feature-flag'; @Component({ selector: 'app-feature-toggle-example', - styles: [` - .sample-form .form-horizontal .form-group { - margin-left: 0px; - } - .padding-top-15 { - padding-top: 15px; - } - .padding-bottom-15 { - padding-bottom: 15px; - } - `], + styles: [ + ` + .sample-form .form-horizontal .form-group { + margin-left: 0px; + } + .padding-top-15 { + padding-top: 15px; + } + .padding-bottom-15 { + padding-bottom: 15px; + } + ` + ], templateUrl: './feature-toggle.example.component.html' }) export class FeatureToggleExampleComponent implements OnInit { @@ -30,9 +28,9 @@ export class FeatureToggleExampleComponent implements OnInit { featureFlagEnable: boolean = true; featureFlagEnablementLevel: string = 'beta'; userLevel: string = 'internal'; + showFeatureOptIn: boolean = false; - constructor(private featureToggleService: FeatureTogglesService, private cd: ChangeDetectorRef) { - } + constructor(private featureToggleService: FeatureTogglesService, private cd: ChangeDetectorRef) {} ngOnInit(): void { (this.featureToggleService as any).featureFlagName = this.featureFlagName; @@ -45,16 +43,21 @@ export class FeatureToggleExampleComponent implements OnInit { if ((this.featureToggleService as any).featureFlagName === undefined) { return; } - if (this.featureFlagEnablementLevel !== 'internal' && + if ( + this.featureFlagEnablementLevel !== 'internal' && this.featureFlagEnablementLevel !== 'experimental' && this.featureFlagEnablementLevel !== 'beta' && - this.featureFlagEnablementLevel !== 'released') { - this.featureFlagEnablementLevel = (this.featureToggleService as any).featureFlagEnablementLevel; // keep previous valid version + this.featureFlagEnablementLevel !== 'released' + ) { + this.featureFlagEnablementLevel = (this + .featureToggleService as any).featureFlagEnablementLevel; // keep previous valid version } - if (this.userLevel !== 'internal' && + if ( + this.userLevel !== 'internal' && this.userLevel !== 'experimental' && this.userLevel !== 'beta' && - this.userLevel !== 'released') { + this.userLevel !== 'released' + ) { this.userLevel = (this.featureToggleService as any).userLevel; // keep previous valid version } (this.featureToggleService as any).featureFlagName = this.featureFlagName; diff --git a/src/app/feature-toggle/feature-toggle.example.module.ts b/src/app/feature-toggle/feature-toggle.example.module.ts index e0deaf8..1a3c523 100644 --- a/src/app/feature-toggle/feature-toggle.example.module.ts +++ b/src/app/feature-toggle/feature-toggle.example.module.ts @@ -3,9 +3,10 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { TabsetConfig, TabsModule } from 'ngx-bootstrap/tabs'; -import { FeatureFlagModule } from '../../../projects/ngx-feature-flag/src/lib/feature-flag.module'; +import { FeatureFlagModule } from 'ngx-feature-flag'; import { DemoComponentsModule } from '../welcome/demo-components.module'; import { FeatureToggleExampleComponent } from './feature-toggle.example.component'; +import { UserService, AuthenticationService } from 'ngx-login-client'; @NgModule({ imports: [ @@ -17,7 +18,7 @@ import { FeatureToggleExampleComponent } from './feature-toggle.example.componen ], declarations: [FeatureToggleExampleComponent], exports: [FeatureToggleExampleComponent], - providers: [TabsetConfig] + providers: [TabsetConfig, UserService, AuthenticationService] }) export class FeatureToggleExampleModule { constructor() {} diff --git a/src/app/feature-wrapper/feature-toggle.component.spec.ts b/src/app/feature-wrapper/feature-toggle.component.spec.ts index ab05913..cb9b5db 100644 --- a/src/app/feature-wrapper/feature-toggle.component.spec.ts +++ b/src/app/feature-wrapper/feature-toggle.component.spec.ts @@ -1,52 +1,107 @@ -import { Component } from '@angular/core'; +import { HttpClientModule } from '@angular/common/http'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { HttpModule } from '@angular/http'; -import { Observable } from 'rxjs'; -import { FeatureTogglesService } from '../service/feature-toggles.service'; -import { FeatureToggleComponent } from './feature-toggle.component'; +import { of } from 'rxjs'; +import { Feature } from 'ngx-feature-flag'; +import { FeatureTogglesService } from 'ngx-feature-flag'; +import { FeatureToggleComponent } from 'ngx-feature-flag'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: `f8-host-component`, + template: ` + +
My content here
+ ` +}) +class TestHostComponent1 {} + +@Component({ + selector: `f8-host-component`, + template: ` + + +
My content here
+ ` +}) +class TestHostComponent2 {} + +@Component({ + selector: `f8-feature-warning-page`, + template: ` +
Warning Page!
+ ` +}) +class TestWarningComponent {} describe('FeatureToggleComponent', () => { - let featureServiceMock: jasmine.SpyObj; - let hostFixture: ComponentFixture; - - @Component({ - selector: `host-component`, - template: `
My content here
` - }) - class TestHostComponent { - } + let mockFeatureService: jasmine.SpyObj; + let hostFixture1: ComponentFixture; + let hostFixture2: ComponentFixture; + + const feature1: Feature = { + attributes: { + name: 'Planner', + description: 'Description', + enabled: true, + 'enablement-level': 'beta', + 'user-enabled': true + }, + id: 'Planner' + }; + + const feature2: Feature = { + attributes: { + name: 'Planner Query', + description: 'Description', + enabled: true, + 'enablement-level': 'internal', + 'user-enabled': false + }, + id: 'PlannerQuery' + }; + beforeEach(() => { - featureServiceMock = jasmine.createSpyObj('FeatureTogglesService', ['isFeatureUserEnabled']); + mockFeatureService = jasmine.createSpyObj('FeatureTogglesService', ['getFeature']); TestBed.configureTestingModule({ - imports: [FormsModule, HttpModule], - declarations: [FeatureToggleComponent, TestHostComponent], - providers: [ - { - provide: FeatureTogglesService, useValue: featureServiceMock - } - ] + imports: [FormsModule, HttpClientModule, RouterModule], + declarations: [ + FeatureToggleComponent, + TestHostComponent1, + TestHostComponent2, + TestWarningComponent + ], + providers: [{ provide: FeatureTogglesService, useValue: mockFeatureService }], + schemas: [NO_ERRORS_SCHEMA] }); - hostFixture = TestBed.createComponent(TestHostComponent); + hostFixture1 = TestBed.createComponent(TestHostComponent1); + hostFixture2 = TestBed.createComponent(TestHostComponent2); }); it('should render content if feature is user enabled', async(() => { - // given - featureServiceMock.isFeatureUserEnabled.and.returnValue(Observable.of(true)); - hostFixture.detectChanges(); - hostFixture.whenStable().then(() => { - expect(hostFixture.nativeElement.querySelector('div').innerText).toEqual('My content here'); + mockFeatureService.getFeature.and.returnValue(of(feature1)); + hostFixture1.detectChanges(); + hostFixture1.whenStable().then(() => { + expect(hostFixture1.nativeElement.querySelector('div').innerText).toEqual('My content here'); }); })); it('should not render content if feature is not user enabled', async(() => { - // given - featureServiceMock.isFeatureUserEnabled.and.returnValue(Observable.of(false)); - hostFixture.detectChanges(); - hostFixture.whenStable().then(() => { - expect(hostFixture.nativeElement.querySelector('div')).toBeNull(); + mockFeatureService.getFeature.and.returnValue(of(feature2)); + hostFixture1.detectChanges(); + hostFixture1.whenStable().then(() => { + expect(hostFixture1.nativeElement.querySelector('div')).toBeNull(); + }); + })); + + it('should render default warning template when showFeatureOptIn is true', async(() => { + mockFeatureService.getFeature.and.returnValue(of(feature2)); + hostFixture2.detectChanges(); + hostFixture2.whenStable().then(() => { + expect(hostFixture2.nativeElement.querySelector('div').innerText).toEqual('Warning Page!'); }); })); }); diff --git a/src/app/service/enable-feature-mock.service.ts b/src/app/service/enable-feature-mock.service.ts new file mode 100644 index 0000000..785de16 --- /dev/null +++ b/src/app/service/enable-feature-mock.service.ts @@ -0,0 +1,36 @@ +import { Injectable } from '@angular/core'; +import { Profile, User } from 'ngx-login-client'; +import { Observable, of } from 'rxjs'; + +interface ExtUserResponse { + data: ExtUser; +} + +export class ExtUser extends User { + attributes: ExtProfile; +} + +export class ExtProfile extends Profile { + contextInformation: any; + registrationCompleted: boolean; + featureLevel: string; +} + +@Injectable() +export class EnableFeatureMockService { + createTransientProfile(): ExtProfile { + const profile = { + registrationCompleted: false + } as ExtProfile; + return profile; + } + + update(profile: ExtProfile): Observable { + const response = { + attributes: { + featureLevel: profile.featureLevel + } as ExtProfile + } as ExtUser; + return of(response); + } +} diff --git a/src/app/service/feature-toggles-mock.service.ts b/src/app/service/feature-toggles-mock.service.ts index 139b44c..cd04d5d 100644 --- a/src/app/service/feature-toggles-mock.service.ts +++ b/src/app/service/feature-toggles-mock.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { Feature } from '../../../projects/ngx-feature-flag/src/lib//models/feature'; +import { Feature } from 'ngx-feature-flag'; @Injectable() -export class FeatureTogglesServiceMock { +export class FeatureTogglesServiceMock { featureFlagName: string; featureFlagEnablementLevel: string; featureFlagEnable: boolean; @@ -20,13 +20,20 @@ export class FeatureTogglesServiceMock { return true; } if (this.userLevel === 'beta') { - if (this.featureFlagEnablementLevel === 'internal' || this.featureFlagEnablementLevel === 'experimental') { + if ( + this.featureFlagEnablementLevel === 'internal' || + this.featureFlagEnablementLevel === 'experimental' + ) { return false; } return true; } if (this.userLevel === 'released') { - if (this.featureFlagEnablementLevel === 'internal' || this.featureFlagEnablementLevel === 'experimental' || this.featureFlagEnablementLevel === 'beta') { + if ( + this.featureFlagEnablementLevel === 'internal' || + this.featureFlagEnablementLevel === 'experimental' || + this.featureFlagEnablementLevel === 'beta' + ) { return false; } return true; @@ -34,13 +41,13 @@ export class FeatureTogglesServiceMock { return false; } getFeature(id: string): Observable { - const feature = { + const feature = { attributes: { 'user-enabled': this.isUserLevelEnabled(), - 'enabled': this.featureFlagEnable, + enabled: this.featureFlagEnable, 'enablement-level': this.featureFlagEnablementLevel, - 'description': 'description', - 'name': this.featureFlagName + description: 'description', + name: this.featureFlagName }, id: this.featureFlagName } as Feature; @@ -51,13 +58,13 @@ export class FeatureTogglesServiceMock { return of(); } isFeatureUserEnabled(id: string): Observable<{} | boolean> { - const feature = { + const feature = { attributes: { 'user-enabled': this.isUserLevelEnabled(), - 'enabled': this.featureFlagEnable, + enabled: this.featureFlagEnable, 'enablement-level': this.featureFlagEnablementLevel, - 'description': 'description', - 'name': this.featureFlagName + description: 'description', + name: this.featureFlagName }, id: this.featureFlagName } as Feature; diff --git a/src/app/service/notifications-mock.service.ts b/src/app/service/notifications-mock.service.ts new file mode 100644 index 0000000..bbcd488 --- /dev/null +++ b/src/app/service/notifications-mock.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class NotificationsMockService { + message(notification) { + console.log(notification['message']); + } +}