Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit 9e12276

Browse files
ghillertcppwfs
authored andcommitted
gh-527 Ensure that the feature toggle works for the Dashboard
* Add feature toggle - uses the `/about` REST endpoint - treats feature toggle like an authorization concern - uses `RolesDirective` to make checks * Add tests gh-527 Polishing based on SCDF #1829 gh-527 Code review changes gh-527 Fix tests
1 parent 37bc508 commit 9e12276

28 files changed

+480
-115
lines changed

ui/src/app/about/about-details.component.html

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,11 @@ <h2 *ngIf="!dataflowVersionInfo" class="text-warning" id="serverWarningError">Ob
77
<table class="table table-hover" id="dataFlowVersionTable">
88
<tbody>
99
<tr>
10-
<td class="col-xs-6">Name</td><td>{{dataflowVersionInfo.versionInfo.implementationDependency.name}}</td>
10+
<td class="col-xs-6">Name</td><td>{{dataflowVersionInfo.versionInfo.implementation.name}}</td>
1111
</tr>
1212
<tr>
13-
<td class="col-xs-6">Version</td><td>{{dataflowVersionInfo.versionInfo.implementationDependency.version}}</td>
13+
<td class="col-xs-6">Version</td><td>{{dataflowVersionInfo.versionInfo.implementation.version}}</td>
1414
</tr>
15-
<tr>
16-
<td class="col-xs-6">Checksum Sha1</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha1 ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha1 : 'N/A' : 'N/A'}}</td>
17-
</tr>
18-
<tr>
19-
<td class="col-xs-6">Checksum Sha256</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha256 ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha256 : 'N/A' : 'N/A'}}</td>
20-
</tr>
21-
2215
</tbody>
2316
</table>
2417

@@ -99,48 +92,24 @@ <h2>Version Information</h2>
9992
<tbody>
10093
<tr>
10194
<td class="col-xs-6">Implementation Version</td>
102-
<td>{{dataflowVersionInfo.versionInfo.implementationDependency.version}} ({{dataflowVersionInfo.versionInfo.implementationDependency.name}})</td>
95+
<td>{{dataflowVersionInfo.versionInfo.implementation.version}} ({{dataflowVersionInfo.versionInfo.implementation.name}})</td>
10396
</tr>
10497
<tr>
105-
<td class="col-xs-6">Implementation Checksum Sha1</td>
106-
<td>{{dataflowVersionInfo.versionInfo.implementationDependency.checksumSha1 ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha1 : 'N/A'}}</td>
98+
<td class="col-xs-6">Core</td><td>{{dataflowVersionInfo.versionInfo.core.version}} ({{dataflowVersionInfo.versionInfo.core.name}})</td>
10799
</tr>
108100
<tr>
109-
<td class="col-xs-6">Implementation Checksum Sha256</td>
110-
<td>{{dataflowVersionInfo.versionInfo.implementationDependency.checksumSha256 ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha256 : 'N/A'}}</td>
101+
<td class="col-xs-6">Dashboard</td><td>{{dataflowVersionInfo.versionInfo.dashboard.version}} ({{dataflowVersionInfo.versionInfo.dashboard.name}})</td>
111102
</tr>
112103
<tr>
113-
<td class="col-xs-6">Core</td><td>{{dataflowVersionInfo.versionInfo.coreDependency.version}} ({{dataflowVersionInfo.versionInfo.coreDependency.name}})</td>
104+
<td class="col-xs-6">Shell</td><td>{{dataflowVersionInfo.versionInfo.shell.version}} ({{dataflowVersionInfo.versionInfo.shell.name}})</td>
114105
</tr>
115-
<tr>
116-
<td class="col-xs-6">Core Checksum Sha1</td>
117-
<td>{{dataflowVersionInfo.versionInfo.coreDependency.checksumSha1 ? dataflowVersionInfo.versionInfo.coreDependency.checksumSha1 : 'N/A'}}</td>
118-
</tr>
119-
<tr>
120-
<td class="col-xs-6">Core Checksum Sha256</td>
121-
<td>{{dataflowVersionInfo.versionInfo.coreDependency.checksumSha256 ? dataflowVersionInfo.versionInfo.coreDependency.checksumSha256 : 'N/A'}}</td>
122-
</tr>
123-
<tr>
124-
<td class="col-xs-6">Dashboard</td><td>{{dataflowVersionInfo.versionInfo.dashboardDependency.version}} ({{dataflowVersionInfo.versionInfo.dashboardDependency.name}})</td>
125-
</tr>
126-
<tr>
127-
<td class="col-xs-6">Dashboard Checksum Sha1</td>
128-
<td>{{dataflowVersionInfo.versionInfo.dashboardDependency.checksumSha1 ? dataflowVersionInfo.versionInfo.dashboardDependency.checksumSha1 : 'N/A'}}</td>
129-
</tr>
130-
<tr>
131-
<td class="col-xs-6">Dashboard Checksum Sha256</td>
132-
<td>{{dataflowVersionInfo.versionInfo.dashboardDependency.checksumSha256 ? dataflowVersionInfo.versionInfo.dashboardDependency.checksumSha256 : 'N/A'}}</td>
133-
</tr>
134-
<tr>
135-
<td class="col-xs-6">Shell</td><td>{{dataflowVersionInfo.versionInfo.shellDependency.version}} ({{dataflowVersionInfo.versionInfo.shellDependency.name}})</td>
136-
</tr>
137-
<tr>
106+
<tr *ngIf="dataflowVersionInfo.versionInfo.shell.checksumSha1">
138107
<td class="col-xs-6">Shell Checksum Sha1</td>
139-
<td>{{dataflowVersionInfo.versionInfo.shellDependency.checksumSha1 ? dataflowVersionInfo.versionInfo.shellDependency.checksumSha1 : 'N/A'}}</td>
108+
<td>{{dataflowVersionInfo.versionInfo.shell.checksumSha1}}</td>
140109
</tr>
141-
<tr>
110+
<tr *ngIf="dataflowVersionInfo.versionInfo.shell.checksumSha256">
142111
<td class="col-xs-6">Shell Checksum Sha256</td>
143-
<td>{{dataflowVersionInfo.versionInfo.shellDependency.checksumSha256 ? dataflowVersionInfo.versionInfo.shellDependency.checksumSha256 : 'N/A'}}</td>
112+
<td>{{dataflowVersionInfo.versionInfo.shell.checksumSha256}}</td>
144113
</tr>
145114
</tbody>
146115
</table>

ui/src/app/about/about-details.component.spec.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ describe('AboutDetailsComponent', () => {
5454
expect(component).toBeTruthy();
5555

5656
// verify Data Flow Server Implementation
57-
validateColumnValues('dataFlowVersionTable', ['Name', 'Version', 'Checksum Sha1', 'Checksum Sha256'], 0);
58-
validateColumnValues('dataFlowVersionTable', ['FOO', 'BAR', '', '' ], 1);
57+
validateColumnValues('dataFlowVersionTable', ['Name', 'Version'], 0);
58+
validateColumnValues('dataFlowVersionTable', ['FOO', 'BAR'], 1);
5959

6060
// Verify Enabled Features
6161
validateColumnValues('enabledFeaturesTable', ['Analytics', 'Streams', 'Tasks'], 0);
@@ -69,12 +69,10 @@ describe('AboutDetailsComponent', () => {
6969
validateTdValue('roles', 'base_role');
7070

7171
// Starting Version
72-
validateColumnValues('versionInformationTable', ['Implementation', 'Implementation Checksum Sha1',
73-
'Implementation Checksum Sha256', 'Core', 'Core Checksum Sha1', 'Core Checksum Sha256', 'Dashboard',
74-
'Dashboard Checksum Sha1', 'Dashboard Checksum Sha256', 'Shell', 'Shell Checksum Sha1', 'Shell Checksum Sha256'], 0);
75-
validateColumnValues('versionInformationTable', ['BAR (FOO)', 'checksumSample1',
76-
'checksumSample256', 'BOO (BAZ)', 'checksumSample1', 'checksumSample256', 'QIX (QUE)', 'checksumSample1',
77-
'checksumSample256', 'QUUX (QUX)', 'checksumSample1', 'checksumSample256'], 1);
72+
validateColumnValues('versionInformationTable', ['Implementation',
73+
'Core', 'Dashboard',
74+
'Shell', 'Shell Checksum Sha1', 'Shell Checksum Sha256'], 0);
75+
validateColumnValues('versionInformationTable', ['BAR (FOO)', 'BOO (BAZ)', 'QIX (QUE)', 'QUUX (QUX)', 'checksumSample1', 'checksumSample256'], 1);
7876

7977
// App Deployer Table
8078
validateColumnValues('appDeployerTable', ['Implementation Version', 'Name', 'Spi Version',

ui/src/app/about/about.component.html

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,10 @@ <h2 *ngIf="!dataflowVersionInfo" class="text-warning" id="serverWarningError">Ob
1212
<table id="dataFlowVersionTable" class="table table-hover" *ngIf="dataflowVersionInfo">
1313
<tbody>
1414
<tr>
15-
<td>Name</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementationDependency.name : 'N/A'}}</td>
15+
<td>Name</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementation.name : 'N/A'}}</td>
1616
</tr>
1717
<tr>
18-
<td>Version</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementationDependency.version : 'N/A'}}</td>
19-
</tr>
20-
<tr>
21-
<td>Checksum Sha1</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha1 ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha1 : 'N/A' : 'N/A'}}</td>
22-
</tr>
23-
<tr>
24-
<td>Checksum Sha256</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha256 ? dataflowVersionInfo.versionInfo.implementationDependency.checksumSha256 : 'N/A' : 'N/A'}}</td>
18+
<td>Version</td><td>{{dataflowVersionInfo ? dataflowVersionInfo.versionInfo.implementation.version : 'N/A'}}</td>
2519
</tr>
2620
</tbody>
2721
</table>
@@ -35,8 +29,8 @@ <h2 *ngIf="!dataflowVersionInfo" class="text-warning" id="serverWarningError">Ob
3529
<h2 style="margin-top: 1em;" *ngIf="dataflowVersionInfo">Get the Spring Cloud Data Flow Shell</h2>
3630

3731
<p *ngIf="dataflowVersionInfo" class="index-page--subtitle">As an alternative to the Dashboard UI, you can also
38-
<a href="{{dataflowVersionInfo.versionInfo.shellDependency.url}}"
39-
target="_blank">download the compatible version of the Shell</a> ({{dataflowVersionInfo.versionInfo.shellDependency.version}}).</p>
32+
<a href="{{dataflowVersionInfo.versionInfo.shell.url}}"
33+
target="_blank">download the compatible version of the Shell</a> ({{dataflowVersionInfo.versionInfo.shell.version}}).</p>
4034

4135
<h2 *ngIf="dataflowVersionInfo">Need Help or Found an Issue?</h2>
4236

@@ -49,10 +43,10 @@ <h2 *ngIf="dataflowVersionInfo">Need Help or Found an Issue?</h2>
4943
<td>Sources</td><td><a target="_blank" href="https://github.com/spring-cloud/spring-cloud-dataflow">https://github.com/spring-cloud/spring-cloud-dataflow</a></td>
5044
</tr>
5145
<tr>
52-
<td>Documentation</td><td><a target="_blank" href="http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.coreDependency.version}}/reference/htmlsingle/">http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.coreDependency.version}}/reference/htmlsingle/</a></td>
46+
<td>Documentation</td><td><a target="_blank" href="http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.core.version}}/reference/htmlsingle/">http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.core.version}}/reference/htmlsingle/</a></td>
5347
</tr>
5448
<tr>
55-
<td>API Docs</td><td><a target="_blank" href="http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.coreDependency.version}}/api/">http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.coreDependency.version}}/api/</a></td>
49+
<td>API Docs</td><td><a target="_blank" href="http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.core.version}}/api/">http://docs.spring.io/spring-cloud-dataflow/docs/{{dataflowVersionInfo.versionInfo.core.version}}/api/</a></td>
5650
</tr>
5751
<tr>
5852
<td>Support Forum</td><td><a target="_blank" href="http://stackoverflow.com/questions/tagged/spring-cloud-dataflow">http://stackoverflow.com/questions/tagged/spring-cloud-dataflow</a></td>

ui/src/app/about/about.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('AboutComponent', () => {
4646
fixture.detectChanges();
4747
expect(component).toBeTruthy();
4848
let des: DebugElement[] = fixture.debugElement.queryAll(By.css('table[id=dataFlowVersionTable] td'));
49-
expect(des.length).toBe(8);
49+
expect(des.length).toBe(4);
5050
expect(des[0].nativeElement.textContent).toContain('Name');
5151
expect(des[1].nativeElement.textContent).toContain('FOO');
5252
expect(des[2].nativeElement.textContent).toContain('Version');

ui/src/app/about/about.service.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { AboutService } from './about.service';
22
import { Observable } from 'rxjs/Rx';
33
import { ErrorHandler } from '../shared/model/error-handler';
44
import { Response, ResponseOptions } from '@angular/http';
5+
import { SharedAboutService } from '../shared/services/shared-about.service';
56

67
describe('AboutService', () => {
78

@@ -101,7 +102,8 @@ describe('AboutService', () => {
101102
beforeEach(() => {
102103
this.mockHttp = jasmine.createSpyObj('mockHttp', ['get']);
103104
const errorHandler = new ErrorHandler();
104-
this.aboutService = new AboutService(this.mockHttp, errorHandler);
105+
this.sharedAboutService = new SharedAboutService(this.mockHttp, errorHandler);
106+
this.aboutService = new AboutService(this.sharedAboutService, this.mockHttp, errorHandler);
105107
});
106108

107109
it('should call the about service with the right url', () => {

ui/src/app/about/about.service.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
11
import { Injectable } from '@angular/core';
22
import { Http, Response } from '@angular/http';
33
import { Observable } from 'rxjs/Observable';
4+
import { Subject } from 'rxjs/Subject';
5+
46
import 'rxjs/add/operator/catch';
57
import 'rxjs/add/operator/map';
68
import {ErrorHandler} from '../shared/model/error-handler';
79

10+
import { SharedAboutService } from '../shared/services/shared-about.service';
11+
import { FeatureInfo } from '../shared/model/about/feature-info.model';
12+
813
@Injectable()
914
export class AboutService {
1015

11-
private aboutUrl = '/about';
16+
constructor(
17+
private sharedAboutService: SharedAboutService,
18+
private http: Http, private errorHandler: ErrorHandler) {}
1219

13-
constructor(private http: Http, private errorHandler: ErrorHandler) {}
20+
public get aboutInfo(): any {
21+
return this.sharedAboutService.aboutInfo;
22+
}
1423

15-
getAboutInfo(): Observable<any[]> {
16-
return this.http.get(this.aboutUrl)
17-
.map(this.extractData)
18-
.catch(this.errorHandler.handleError);
24+
public set aboutInfo(aboutInfo) {
25+
this.sharedAboutService.aboutInfo = aboutInfo;
1926
}
2027

21-
getDetails(): Observable<any[]> {
22-
return this.http.get(this.aboutUrl)
23-
.map(this.extractData)
24-
.catch(this.errorHandler.handleError);
28+
public get featureInfo(): FeatureInfo {
29+
return this.sharedAboutService.featureInfo;
2530
}
2631

27-
private extractData(res: Response) {
28-
console.log('extract data', res);
29-
const body = res.json();
30-
return body;
32+
public set featureInfo(featureInfo: FeatureInfo) {
33+
this.sharedAboutService.featureInfo = featureInfo;
3134
}
3235

36+
public get featureInfoSubject(): Subject<FeatureInfo> {
37+
return this.sharedAboutService.featureInfoSubject;
38+
}
39+
40+
getAboutInfo(): Observable<any> {
41+
return this.sharedAboutService.getAboutInfo();
42+
}
43+
44+
getDetails(): Observable<any> {
45+
return this.http.get(this.sharedAboutService.aboutUrl)
46+
.map(this.sharedAboutService.extractData.bind(this))
47+
.catch(this.errorHandler.handleError);
48+
}
3349
}

ui/src/app/app.component.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
<div id="navbar" class="collapse navbar-collapse" [ngClass]="{'in': !isCollapsed}">
1414
<ul class="nav navbar-nav navbar-right" (click)="collapse()">
1515
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="apps">Apps</a></li>
16-
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="runtime/apps">Runtime</a></li>
17-
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="streams">Streams</a></li>
18-
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="tasks">Tasks</a></li>
19-
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="jobs">Jobs</a></li>
20-
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="analytics">Analytics</a></li>
16+
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']" appFeature="streamsEnabled"><a routerLink="runtime/apps">Runtime</a></li>
17+
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']" appFeature="streamsEnabled"><a routerLink="streams">Streams</a></li>
18+
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']" appFeature="tasksEnabled"><a routerLink="tasks">Tasks</a></li>
19+
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']" appFeature="tasksEnabled"><a routerLink="jobs">Jobs</a></li>
20+
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']" appFeature="analyticsEnabled"><a routerLink="analytics">Analytics</a></li>
2121
<li routerLinkActive="active" [appRoles]="['ROLE_VIEW']"><a routerLink="about">About</a></li>
2222
<li *ngIf="securityInfo.isAuthenticationEnabled"
2323
dropdown

ui/src/app/apps/apps-routing.module.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ const appsRoutes: Routes = [
1414
canActivate: [AuthGuard],
1515
data: {
1616
authenticate: true,
17-
roles: ['ROLE_VIEW'],
18-
feature: 'appsEnabled'
17+
roles: ['ROLE_VIEW']
1918
},
2019
children: [
2120
{ path: '', component: AppsComponent },

ui/src/app/auth/auth-routing.module.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { NgModule } from '@angular/core';
33
import { RouterModule, Routes } from '@angular/router';
44

5+
import { FeatureDisabledComponent } from './feature-disabled.component';
56
import { LoginComponent } from './login.component';
67
import { LogoutComponent } from './logout.component';
78
import { RolesMissingComponent } from './roles-missing.component';
@@ -15,6 +16,10 @@ const authRoutes: Routes = [
1516
path: 'logout',
1617
component: LogoutComponent
1718
},
19+
{
20+
path: 'feature-disabled',
21+
component: FeatureDisabledComponent
22+
},
1823
{
1924
path: 'roles-missing',
2025
component: RolesMissingComponent

ui/src/app/auth/auth.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { SharedModule } from '../shared/shared.module';
44
import { LoginComponent } from './login.component';
55
import { LogoutComponent } from './logout.component';
66
import { AuthRoutingModule } from './auth-routing.module';
7+
import { FeatureDisabledComponent } from './feature-disabled.component';
78
import { RolesDirective } from './directives/roles.directive';
89
import { RolesMissingComponent } from './roles-missing.component';
910
import { CapsLockDirective } from './directives/caps-lock.directive';
@@ -17,6 +18,7 @@ import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
1718
],
1819
declarations: [
1920
CapsLockDirective,
21+
FeatureDisabledComponent,
2022
LoginComponent,
2123
LogoutComponent,
2224
RolesDirective,

0 commit comments

Comments
 (0)