Added logout
Change-Id: If09690e089976244ded58a27b1f35a3a850ae2d1
diff --git a/conf/app/interfaces.ts b/conf/app/interfaces.ts
index 1080481..39f085d 100644
--- a/conf/app/interfaces.ts
+++ b/conf/app/interfaces.ts
@@ -1,8 +1,10 @@
import {IXosNavigationRoute} from '../../src/app/core/services/navigation';
export interface IStyleConfig {
projectName: string;
+ payoff: string;
favicon: string;
background: string;
+ logo: string;
routes: IXosNavigationRoute[];
}
diff --git a/conf/app/style.config.cord.ts b/conf/app/style.config.cord.ts
index 68c568f..c43cac8 100644
--- a/conf/app/style.config.cord.ts
+++ b/conf/app/style.config.cord.ts
@@ -3,6 +3,8 @@
projectName: 'CORD',
favicon: 'cord-favicon.png',
background: 'cord-bg.jpg',
+ payoff: 'Your VNF orchestrator',
+ logo: 'cord-logo.png',
routes: [
{
label: 'Slices',
diff --git a/conf/app/style.config.opencloud.ts b/conf/app/style.config.opencloud.ts
index 2585dd2..c001344 100644
--- a/conf/app/style.config.opencloud.ts
+++ b/conf/app/style.config.opencloud.ts
@@ -3,6 +3,8 @@
projectName: 'OpenCloud',
favicon: 'opencloud-favicon.png',
background: 'opencloud-bg.jpg',
+ payoff: 'Your OS resource manager',
+ logo: 'opencloud-logo.png',
routes: [
{
label: 'Slices',
diff --git a/package.json b/package.json
index fc73480..1ce1f7c 100644
--- a/package.json
+++ b/package.json
@@ -1,4 +1,5 @@
{
+ "version": "1.0.0",
"dependencies": {
"angular": "^1.5.0",
"angular-animate": "^1.6.0",
diff --git a/src/app/config/interfaces.ts b/src/app/config/interfaces.ts
index f1e27ad..ddf3126 100644
--- a/src/app/config/interfaces.ts
+++ b/src/app/config/interfaces.ts
@@ -1,8 +1,10 @@
import {IXosNavigationRoute} from '../core/services/navigation';
export interface IStyleConfig {
projectName: string;
+ payoff: string;
favicon: string;
background: string;
+ logo: string;
routes: IXosNavigationRoute[];
}
diff --git a/src/app/config/style.config.ts b/src/app/config/style.config.ts
index 2585dd2..c43cac8 100644
--- a/src/app/config/style.config.ts
+++ b/src/app/config/style.config.ts
@@ -1,12 +1,22 @@
import {IStyleConfig} from './interfaces';
export const StyleConfig: IStyleConfig = {
- projectName: 'OpenCloud',
- favicon: 'opencloud-favicon.png',
- background: 'opencloud-bg.jpg',
+ projectName: 'CORD',
+ favicon: 'cord-favicon.png',
+ background: 'cord-bg.jpg',
+ payoff: 'Your VNF orchestrator',
+ logo: 'cord-logo.png',
routes: [
{
label: 'Slices',
state: 'xos.core.slices'
+ },
+ {
+ label: 'Instances',
+ state: 'xos.core.instances'
+ },
+ {
+ label: 'Nodes',
+ state: 'xos.core.nodes'
}
]
};
diff --git a/src/app/core/header/header.html b/src/app/core/header/header.html
index db25904..0a90822 100644
--- a/src/app/core/header/header.html
+++ b/src/app/core/header/header.html
@@ -27,8 +27,8 @@
<minimaliza-menu></minimaliza-menu>
</div>
<a class="navbar-brand" href="#/">
- <em class="brand-title">{{vm.title}}</em>
- <span>v.1.2</span>
+ <img class="img-responsive" ng-src="{{vm.getLogo()}}" alt="">
+ <span>v.{{vm.version}}</span>
</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
@@ -45,7 +45,7 @@
<!--</li>-->
<li class=" profil-link">
<a ui-sref="commonviews.login">
- <span class="profile-address">luna@company.io</span>
+ <span class="profile-address">{{vm.userEmail}}</span>
<img src="../../images/profile.jpg" class="img-circle" alt="">
</a>
</li>
diff --git a/src/app/core/header/header.scss b/src/app/core/header/header.scss
index 8a1664f..a1be115 100644
--- a/src/app/core/header/header.scss
+++ b/src/app/core/header/header.scss
@@ -1,3 +1,21 @@
#toast-container [toast]:not(:first-child) {
margin-top: 10px !important;
+}
+
+xos-header {
+ .navbar-brand {
+ padding: 10px 30px !important;
+ position: relative;
+ background: #24262d !important;
+ span {
+ position: absolute;
+ right: 30px;
+ bottom: 0;
+ color: #24262d;
+ }
+ }
+
+ .navbar-default {
+ background: #2a2d35 !important;
+ }
}
\ No newline at end of file
diff --git a/src/app/core/header/header.spec.ts b/src/app/core/header/header.spec.ts
index c8dffe7..c115a4e 100644
--- a/src/app/core/header/header.spec.ts
+++ b/src/app/core/header/header.spec.ts
@@ -5,7 +5,6 @@
import * as angular from 'angular';
import 'angular-mocks';
import {xosHeader, INotification} from './header';
-import {StyleConfig} from '../../config/style.config';
import {Subject} from 'rxjs';
let element, scope: angular.IRootScopeService, compile: ng.ICompileService, isolatedScope;
@@ -19,14 +18,16 @@
};
};
-interface ImockToastr {
- info(msg: string, title: string): void;
-}
-
-const MockToastr: ImockToastr = {
+const MockToastr = {
info: jasmine.createSpy('info')
};
+const MockAuth = {
+ getUser: () => {
+ return {email: 'test@xos.us'};
+ }
+};
+
const MockToastrConfig = {};
const infoNotification = {
@@ -36,7 +37,7 @@
pk: 1,
object: {
name: 'TestName',
- backend_status: '1 - Test Status'
+ backend_status: '0 - In Progress'
}
}
};
@@ -48,7 +49,8 @@
.component('xosHeader', xosHeader)
.service('SynchronizerStore', MockStore)
.value('toastr', MockToastr)
- .value('toastrConfig', MockToastrConfig);
+ .value('toastrConfig', MockToastrConfig)
+ .value('AuthService', MockAuth);
angular.mock.module('xosHeader');
});
@@ -63,13 +65,14 @@
isolatedScope.notifications = [];
}));
- it('should render the appropriate title', () => {
- const header = $('a.navbar-brand .brand-title', element).text();
- expect(header.trim()).toEqual(StyleConfig.projectName);
+ it('should render the appropriate logo', () => {
+ const header = $('a.navbar-brand img', element).attr('src');
+ // webpack convert img to base64, how to test?
+ expect(header.trim()).not.toBeNull();
});
- it('should set the appropriate favicon', () => {
- console.log($('#favicon').attr('href'));
+ it('should print user email', () => {
+ expect($('.profile-address', element).text()).toBe('test@xos.us');
});
it('should configure toastr', () => {
diff --git a/src/app/core/header/header.ts b/src/app/core/header/header.ts
index 6e057bc..efaa385 100644
--- a/src/app/core/header/header.ts
+++ b/src/app/core/header/header.ts
@@ -2,24 +2,27 @@
import {StyleConfig} from '../../config/style.config';
import {IWSEvent} from '../../datasources/websocket/global';
import {IStoreService} from '../../datasources/stores/synchronizer.store';
+import {IXosAuthService} from '../../datasources/rest/auth.rest';
export interface INotification extends IWSEvent {
viewed?: boolean;
}
class HeaderController {
- static $inject = ['$scope', 'SynchronizerStore', 'toastr', 'toastrConfig'];
- public title: string;
+ static $inject = ['$scope', 'AuthService', 'SynchronizerStore', 'toastr', 'toastrConfig'];
public notifications: INotification[] = [];
public newNotifications: INotification[] = [];
+ public version: string;
+ public userEmail: string;
constructor(
private $scope: angular.IScope,
+ private authService: IXosAuthService,
private syncStore: IStoreService,
private toastr: ng.toastr.IToastrService,
private toastrConfig: ng.toastr.IToastrConfig
) {
-
+ this.version = require('../../../../package.json').version;
angular.extend(this.toastrConfig, {
newestOnTop: false,
positionClass: 'toast-top-right',
@@ -32,7 +35,7 @@
// tapToDismiss: false
});
- this.title = StyleConfig.projectName;
+ this.userEmail = this.authService.getUser().email;
this.syncStore.query()
.subscribe(
@@ -40,11 +43,11 @@
$scope.$evalAsync(() => {
let toastrMsg: string;
let toastrLevel: string;
- if (event.msg.object.backend_status.indexOf('1') > -1) {
+ if (event.msg.object.backend_status.indexOf('0') > -1) {
toastrMsg = 'Synchronization started for:';
toastrLevel = 'info';
}
- else if (event.msg.object.backend_status.indexOf('0') > -1) {
+ else if (event.msg.object.backend_status.indexOf('1') > -1) {
toastrMsg = 'Synchronization succedeed for:';
toastrLevel = 'success';
}
@@ -56,24 +59,28 @@
if (toastrLevel && toastrMsg) {
this.toastr[toastrLevel](`${toastrMsg} ${event.msg.object.name}`, event.model);
}
- this.notifications.unshift(event);
- this.newNotifications = this.getNewNotifications(this.notifications);
+ // this.notifications.unshift(event);
+ // this.newNotifications = this.getNewNotifications(this.notifications);
});
}
);
}
- // TODO display a list of notification in the template
- public viewNotification = (notification: INotification) => {
- notification.viewed = true;
- this.newNotifications = this.getNewNotifications(this.notifications);
- };
+ public getLogo(): string {
+ return require(`../../images/brand/${StyleConfig.logo}`);
+ }
- private getNewNotifications = (notifications: INotification[]) => {
- return this.notifications.filter((n: INotification) => {
- return !n.viewed;
- });
- };
+ // TODO display a list of notification in the template (if it make sense)
+ // public viewNotification = (notification: INotification) => {
+ // notification.viewed = true;
+ // this.newNotifications = this.getNewNotifications(this.notifications);
+ // };
+ //
+ // private getNewNotifications = (notifications: INotification[]) => {
+ // return this.notifications.filter((n: INotification) => {
+ // return !n.viewed;
+ // });
+ // };
}
export const xosHeader: angular.IComponentOptions = {
diff --git a/src/app/core/login/login.html b/src/app/core/login/login.html
index 6f23959..4fa78dc 100644
--- a/src/app/core/login/login.html
+++ b/src/app/core/login/login.html
@@ -25,12 +25,12 @@
<div class="form-group">
<label class="control-label" for="username">Username</label>
<input type="text" ng-model="username" placeholder="example@gmail.com" title="Please enter you username" required="" value="" name="username" id="username" class="form-control">
- <span class="help-block small">Your unique username to app</span>
+ <!--<span class="help-block small">Your unique username to app</span>-->
</div>
<div class="form-group">
<label class="control-label" for="password">Password</label>
<input type="password" ng-model="password" title="Please enter your password" placeholder="******" required="" value="" name="password" id="password" class="form-control">
- <span class="help-block small">Your strong password</span>
+ <!--<span class="help-block small">Your strong password</span>-->
</div>
<div>
<button ng-click="vm.login(username, password)" class="btn btn-accent">Login</button>
diff --git a/src/app/core/login/login.scss b/src/app/core/login/login.scss
index 88a907a..8fe8a57 100644
--- a/src/app/core/login/login.scss
+++ b/src/app/core/login/login.scss
@@ -2,6 +2,8 @@
.content {
background-repeat: no-repeat;
background-size: cover;
+ background-position: center;
+ min-height: 100%;
}
.container-center {
diff --git a/src/app/core/login/login.ts b/src/app/core/login/login.ts
index 11642f1..334ad08 100644
--- a/src/app/core/login/login.ts
+++ b/src/app/core/login/login.ts
@@ -19,7 +19,6 @@
}
public login(username: string, password: string) {
- console.log(username, password);
this.authService.login({
username: username,
password: password
diff --git a/src/app/core/nav/nav.html b/src/app/core/nav/nav.html
index fe96460..37d2f76 100644
--- a/src/app/core/nav/nav.html
+++ b/src/app/core/nav/nav.html
@@ -22,11 +22,26 @@
</ul>
</li>
<li class="nav-info">
- <i class="pe pe-7s-shield text-accent"></i>
-
- <div class="m-t-xs">
- <span class="c-white">{{vm.appName}}</span>
- monitoring and administration for networks applications.
+ <div class="row">
+ <div class="col-sm-4">
+ <i class="pe pe-7s-shield text-accent"></i>
+ </div>
+ <div class="col-sm-8">
+ <h4 class="c-white">{{vm.appName}}</h4>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12">
+ <span class="c-white">{{vm.payoff}}</span>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-4">
+ <i class="pe pe-7s-lock text-accent"></i>
+ </div>
+ <div class="col-sm-8">
+ <a ng-click="vm.logout()" class="btn btn-accent btn-block btn-logout">Logout</a>
+ </div>
</div>
</li>
</ul>
diff --git a/src/app/core/nav/nav.scss b/src/app/core/nav/nav.scss
index e69de29..7646cd9 100644
--- a/src/app/core/nav/nav.scss
+++ b/src/app/core/nav/nav.scss
@@ -0,0 +1,7 @@
+xos-nav {
+ .nav-info {
+ .row + .row:last-child {
+ margin-top: 20px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/nav/nav.spec.ts b/src/app/core/nav/nav.spec.ts
index 6a70f4c..3c18e4d 100644
--- a/src/app/core/nav/nav.spec.ts
+++ b/src/app/core/nav/nav.spec.ts
@@ -18,12 +18,17 @@
this.query = () => baseRoutes;
};
+const AuthMock = {
+ logout: jasmine.createSpy('logout')
+};
+
describe('Nav component', () => {
beforeEach(() => {
angular
.module('xosNav', ['app/core/nav/nav.html', 'ui.router'])
.component('xosNav', xosNav)
- .service('NavigationService', NavigationService);
+ .service('NavigationService', NavigationService)
+ .value('AuthService', AuthMock);
angular.mock.module('xosNav');
});
@@ -54,4 +59,13 @@
const childRouteContainer = $('.nav-second li', element);
expect(childRouteContainer.length).toBe(1);
});
+
+ it('should call the logout method', () => {
+ // NOTE upgrade to test the ng-click binding
+ // const btn = $(element).find('.nav-info .btn-block');
+ // btn.click();
+ // scope.$digest();
+ isolatedScope.vm.logout();
+ expect(AuthMock.logout).toHaveBeenCalled();
+ });
});
diff --git a/src/app/core/nav/nav.ts b/src/app/core/nav/nav.ts
index edaeb24..43f62b3 100644
--- a/src/app/core/nav/nav.ts
+++ b/src/app/core/nav/nav.ts
@@ -1,17 +1,20 @@
import './nav.scss';
import {IXosNavigationService, IXosNavigationRoute} from '../services/navigation';
import {StyleConfig} from '../../config/style.config';
+import {IXosAuthService} from '../../datasources/rest/auth.rest';
class NavCtrl {
- static $inject = ['$scope', '$state', 'NavigationService'];
+ static $inject = ['$scope', '$state', 'NavigationService', 'AuthService'];
public routes: IXosNavigationRoute[];
public navSelected: string;
public appName: string;
+ public payoff: string;
constructor(
private $scope: ng.IScope,
private $state: angular.ui.IStateService,
- private navigationService: IXosNavigationService
+ private navigationService: IXosNavigationService,
+ private authService: IXosAuthService
) {
// NOTE we'll need to have:
// - Base routes (defined from configuration based on BRAND)
@@ -22,6 +25,7 @@
this.routes = routes;
});
this.appName = StyleConfig.projectName;
+ this.payoff = StyleConfig.payoff;
}
activateRoute(route: IXosNavigationRoute) {
@@ -52,7 +56,10 @@
else {
return true;
}
+ }
+ logout() {
+ this.authService.logout();
}
}
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 9d0c191..f9b76f0 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -74,7 +74,7 @@
prop: f.name
};
- if (f.name === 'id') {
+ if (f.name === 'id' || f.name === 'name') {
// NOTE can we find a better method to generalize?
col.link = item => `#/core${baseUrl.replace(':id?', item.id)}`;
}
@@ -93,6 +93,7 @@
}
};
}
+
return col;
})
.filter(v => angular.isDefined(v));
diff --git a/src/app/core/services/navigation.ts b/src/app/core/services/navigation.ts
index c840878..d184fc2 100644
--- a/src/app/core/services/navigation.ts
+++ b/src/app/core/services/navigation.ts
@@ -23,16 +23,20 @@
constructor() {
const defaultRoutes = [
{
+ label: 'Home',
+ state: 'xos.dashboard'
+ },
+ {
label: 'Core',
state: 'xos.core'
},
- {
- label: 'Home',
- state: 'xos.dashboard'
- }
];
// adding configuration defined routes
- this.routes = StyleConfig.routes.concat(defaultRoutes).reverse();
+ // this.routes = StyleConfig.routes.concat(defaultRoutes).reverse();
+ this.routes = defaultRoutes;
+ StyleConfig.routes.forEach(r => {
+ this.add(r);
+ });
}
query() {
diff --git a/src/app/datasources/rest/auth.rest.spec.ts b/src/app/datasources/rest/auth.rest.spec.ts
index 85aefb4..16c0f8d 100644
--- a/src/app/datasources/rest/auth.rest.spec.ts
+++ b/src/app/datasources/rest/auth.rest.spec.ts
@@ -69,9 +69,9 @@
it('should remove user auth from cookies', (done) => {
service.logout()
.then((res) => {
- expect($cookies.get('xoscsrftoken')).toEqual(null);
- expect($cookies.get('xossessionid')).toEqual(null);
- expect($cookies.get('xosuser')).toEqual(null);
+ expect($cookies.get('xoscsrftoken')).toEqual(undefined);
+ expect($cookies.get('xossessionid')).toEqual(undefined);
+ expect($cookies.get('xosuser')).toEqual(undefined);
done();
})
.catch(e => {
diff --git a/src/app/datasources/rest/auth.rest.ts b/src/app/datasources/rest/auth.rest.ts
index 1792920..e7a593c 100644
--- a/src/app/datasources/rest/auth.rest.ts
+++ b/src/app/datasources/rest/auth.rest.ts
@@ -13,9 +13,15 @@
};
}
+export interface IXosUser {
+ id: number;
+ email: string;
+}
+
export interface IXosAuthService {
login(data: IAuthRequestData): Promise<any>;
logout(): Promise<any>;
+ getUser(): IXosUser;
}
export class AuthService {
@@ -44,7 +50,7 @@
public logout(): Promise<any> {
const d = this.$q.defer();
- this.$http.post(`${AppConfig.apiEndpoint}/utility/login/`, {
+ this.$http.post(`${AppConfig.apiEndpoint}/utility/logout/`, {
xoscsrftoken: this.$cookies.get('xoscsrftoken'),
xossessionid: this.$cookies.get('xossessionid')
})
@@ -59,4 +65,8 @@
});
return d.promise;
}
+
+ public getUser(): IXosUser {
+ return JSON.parse(this.$cookies.get('xosuser'));
+ }
}
diff --git a/src/app/images/brand/cord-logo.png b/src/app/images/brand/cord-logo.png
new file mode 100644
index 0000000..ff0fdf5
--- /dev/null
+++ b/src/app/images/brand/cord-logo.png
Binary files differ
diff --git a/src/app/images/brand/opencloud-logo.png b/src/app/images/brand/opencloud-logo.png
new file mode 100644
index 0000000..a55f86a
--- /dev/null
+++ b/src/app/images/brand/opencloud-logo.png
Binary files differ
diff --git a/src/app/template/directives/directives.ts b/src/app/template/directives/directives.ts
index ef6b628..c2d96cd 100644
--- a/src/app/template/directives/directives.ts
+++ b/src/app/template/directives/directives.ts
@@ -34,7 +34,6 @@
restrict: 'EA',
template: '<div class="left-nav-toggle"><a href ng-click="minimalize()"><i class="stroke-hamburgermenu"></i> </a>',
controller: function ($scope, $element) {
- console.log('mimalize');
$scope.minimalize = function () {
$("body").toggleClass("nav-toggle");
}
diff --git a/src/app/views/crud/crud.html b/src/app/views/crud/crud.html
index 118a6b7..52e9a51 100644
--- a/src/app/views/crud/crud.html
+++ b/src/app/views/crud/crud.html
@@ -8,7 +8,7 @@
<a class="btn btn-default" ng-if="!vm.list" href="{{vm.baseUrl}}">Back</a>
</div>
<div class="header-icon">
- <i class="pe page-header-icon pe-7s-plugin"></i>
+ <i class="pe page-header-icon pe-7s-note2"></i>
</div>
<div class="header-title">
<h3>{{vm.title}}</h3>
@@ -27,9 +27,11 @@
<h4>Related Items: </h4>
</div>
<div class="col-lg-8 text-right">
- <a ng-repeat="r in vm.related" href="#/core/{{r.toLowerCase()}}s/" class="btn btn-default">
- {{r}}
- </a>
+ <div class="btn-group">
+ <a ng-repeat="r in vm.related" href="#/core/{{r.toLowerCase()}}s/" class="btn btn-default">
+ {{r}}
+ </a>
+ </div>
</div>
<hr>
</div>
diff --git a/src/index.scss b/src/index.scss
index a1a3f77..4cc14b8 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -1,3 +1,8 @@
+html, body.blank {
+ height: 100%;
+ min-height: 100%;
+}
+
.content > div {
opacity: 1 !important;
}
\ No newline at end of file
diff --git a/src/interceptors.ts b/src/interceptors.ts
index 7707b0b..57f79f2 100644
--- a/src/interceptors.ts
+++ b/src/interceptors.ts
@@ -33,7 +33,7 @@
if (!$cookies.get('xoscsrftoken') || !$cookies.get('xossessionid')) {
return req;
}
- req.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
+ // req.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
req.headers['x-csrftoken'] = $cookies.get('xoscsrftoken');
req.headers['x-sessionid'] = $cookies.get('xossessionid');
return req;
@@ -44,7 +44,11 @@
export function NoHyperlinksInterceptor() {
return {
request: (req) => {
- if (req.url.indexOf('.html') === -1 && req.url.indexOf('login') !== -1) {
+ if (
+ req.url.indexOf('.html') === -1
+ // && req.url.indexOf('login') === -1
+ // && req.url.indexOf('logout') === -1
+ ) {
// NOTE this may fail if there are already query params
req.url += '?no_hyperlinks=1';
}