Dashboard and nginx fixes
Change-Id: I4ee34b24779f929f41852d4662018caead860ab6
diff --git a/Dockerfile b/Dockerfile
index 4b1df3d..8dd7ce4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -25,4 +25,5 @@
RUN npm run build
# Override nginx configutaion
-RUN mv ${CODE_SOURCE}/nginx.conf /etc/nginx/conf.d/default.conf
\ No newline at end of file
+RUN mv ${CODE_SOURCE}/nginx.conf /etc/nginx/conf.d/default.conf
+#RUN service nginx restart
\ No newline at end of file
diff --git a/README.md b/README.md
index 18837b9..7b369a6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,39 @@
# XOS GUI
-This is the new XOS UI
+**Experimental Feature**
+
+This UI is currently hidden behind a flag and at the current state it is guaranteed as working only from the `frontend` config.
+#### Setup the correct environment:
+- Clone `opencord` using `repo`
+```
+repo init -u https://gerrit.opencord.org/manifest
+repo sync
+```
+- Download XOS patch to enable Redis notifications (waiting for the release to merge it)
+```
+cd orchestration/xos
+git review -d 1852
+```
+
+#### Start the system
+
+A vagrant VM is provided to make your life easier, so:
+```
+cd orchestration/service-profile/frontend
+vagrant up frontend
+vagrant ssh frontend # password is vagrant
+cd service-profile/frontend
+make local_containers
+make
+make experimental-ui
+```
+The new UI is now accessible at `http://<your-ip>` on port `80`
+
+If you want to create some slices to have some data:
+```
+make slices
+```
+
# Development
@@ -16,5 +49,23 @@
- `NODE_ENV`: to configure the app constants (eg: `dev`, `production`)
- `BRAND`: to configure style constants (eg: `cord`, `opencloud`)
+# Test
+To commands come with the project:
+- `npm run test:auto` to watch source files and run test anytime they change, useful for development
+- `npm test` to run test once, this is the command triggered by Jenkins
+## Emulate a Synchronizer notification
+
+```
+redis-cli -h xos.dev
+
+# In progress
+PUBLISH Slice "{\"pk\": 19, \"object\": {\"id\": 19,\"name\": \"mysite_test_redis\", \"backend_status\": \"0 - In Progress\"}, \"changed_fields\": [\"updated\", \"backend_status\"]}"
+
+# Succes
+PUBLISH Slice "{\"pk\": 19, \"object\": {\"id\": 19,\"name\": \"mysite_test_redis\", \"backend_status\": \"1 - Success\"}, \"changed_fields\": [\"updated\", \"backend_status\"]}"
+
+# Error
+PUBLISH Slice "{\"pk\": 19, \"object\": {\"id\": 19,\"name\": \"mysite_test_redis\", \"backend_status\": \"2 - Error\"}, \"changed_fields\": [\"updated\", \"backend_status\"]}"
+```
diff --git a/conf/webpack-dist.conf.js b/conf/webpack-dist.conf.js
index 59bb335..1ee1855 100644
--- a/conf/webpack-dist.conf.js
+++ b/conf/webpack-dist.conf.js
@@ -34,7 +34,9 @@
{
test: /.html$/,
loaders: [
- 'html'
+ 'html?' + JSON.stringify({
+ attrs: ["img:src", "img:ng-src"]
+ })
]
},
{
diff --git a/conf/webpack-test.conf.js b/conf/webpack-test.conf.js
index d3303ba..a87e383 100644
--- a/conf/webpack-test.conf.js
+++ b/conf/webpack-test.conf.js
@@ -25,7 +25,9 @@
{
test: /.html$/,
loaders: [
- 'html'
+ 'html?' + JSON.stringify({
+ attrs: ["img:src", "img:ng-src"]
+ })
]
},
{
diff --git a/nginx.conf b/nginx.conf
index 659bb61..4506b59 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -11,10 +11,12 @@
}
location /api {
+ resolver 127.0.0.1 valid=30s;
proxy_pass http://xos-rest-gw:3000;
}
location /socket.io {
+ resolver 127.0.0.1 valid=30s;
proxy_pass http://xos-rest-gw:3000;
}
}
\ No newline at end of file
diff --git a/src/app/core/header/header.ts b/src/app/core/header/header.ts
index efaa385..849767f 100644
--- a/src/app/core/header/header.ts
+++ b/src/app/core/header/header.ts
@@ -35,7 +35,7 @@
// tapToDismiss: false
});
- this.userEmail = this.authService.getUser().email;
+ this.userEmail = this.authService.getUser() ? this.authService.getUser().email : '';
this.syncStore.query()
.subscribe(
diff --git a/src/app/core/nav/nav.scss b/src/app/core/nav/nav.scss
index 7646cd9..ef3020b 100644
--- a/src/app/core/nav/nav.scss
+++ b/src/app/core/nav/nav.scss
@@ -1,5 +1,18 @@
xos-nav {
+ .navigation {
+ max-height: 100%;
+ display: block;
+ overflow-y: scroll;
+
+ nav {
+ margin-bottom: 200px;
+ }
+ }
+
.nav-info {
+ position: fixed !important;
+ bottom: 0;
+ width: 200px;
.row + .row:last-child {
margin-top: 20px;
}
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index f9b76f0..599edaf 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -30,7 +30,8 @@
'no_policy',
'omf_friendly',
'enabled',
- 'validators'
+ 'validators',
+ 'password'
];
constructor() {
diff --git a/src/app/core/table/table.html b/src/app/core/table/table.html
index 23b5a2f..382dbec 100644
--- a/src/app/core/table/table.html
+++ b/src/app/core/table/table.html
@@ -8,19 +8,20 @@
ng-model="vm.query"/>
</div>
</div>
- <table ng-class="vm.classes">
+ <div class="table-responsive">
+ <table ng-class="vm.classes">
<thead>
<tr>
<th ng-repeat="col in vm.columns">
{{col.label}}
- <span ng-if="vm.config.order">
+ <div ng-if="vm.config.order">
<a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">
<i class="fa fa-chevron-up"></i>
</a>
<a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">
<i class="fa fa-chevron-down"></i>
</a>
- </span>
+ </div>
</th>
<th ng-if="vm.config.actions">Actions:</th>
</tr>
@@ -90,6 +91,7 @@
</tr>
</tbody>
</table>
+ </div>
<!--</div>-->
<!--<div ng-show="(vm.data.length == 0 || !vm.data) && vm.loader == false">-->
<!--<xos-alert config="{type: 'info'}">-->
diff --git a/src/app/core/table/table.scss b/src/app/core/table/table.scss
index fbf6ee7..a2af05e 100644
--- a/src/app/core/table/table.scss
+++ b/src/app/core/table/table.scss
@@ -1,7 +1,10 @@
+xos-table {
+ .row + .table-responsive {
+ margin-top: 10px;
+ }
+}
+
table {
- width: 100%;
- max-width: 100%;
- margin-bottom: 20px;
border-collapse: collapse !important;
background: darken(grey, 20);
border: 1px solid darken(grey, 35);
diff --git a/src/app/core/table/table.ts b/src/app/core/table/table.ts
index dba740e..1ed258d 100644
--- a/src/app/core/table/table.ts
+++ b/src/app/core/table/table.ts
@@ -22,13 +22,15 @@
}
interface IXosTableCgfOrder {
- reverse: boolean;
+ reverse?: boolean;
field: string;
}
export interface IXosTableCfg {
columns: any[];
- order?: IXosTableCgfOrder; // | boolean;
+ order?: IXosTableCgfOrder;
+ filter?: string;
+ actions?: any[]; // TODO create interface
}
class TableCtrl {
diff --git a/src/app/datasources/rest/auth.rest.ts b/src/app/datasources/rest/auth.rest.ts
index e7a593c..f963799 100644
--- a/src/app/datasources/rest/auth.rest.ts
+++ b/src/app/datasources/rest/auth.rest.ts
@@ -21,7 +21,8 @@
export interface IXosAuthService {
login(data: IAuthRequestData): Promise<any>;
logout(): Promise<any>;
- getUser(): IXosUser;
+ getUser(): any; // NOTE how to define return user || false ???
+ isAuthenticated(): boolean;
}
export class AuthService {
@@ -67,6 +68,16 @@
}
public getUser(): IXosUser {
- return JSON.parse(this.$cookies.get('xosuser'));
+ const user = this.$cookies.get('xosuser');
+ if (angular.isDefined(user)) {
+ return JSON.parse(user);
+ }
+ return;
+ }
+
+ public isAuthenticated(): boolean {
+ const token = this.$cookies.get('xoscsrftoken');
+ const session = this.$cookies.get('xossessionid');
+ return angular.isDefined(token) && angular.isDefined(session);
}
}
diff --git a/src/app/datasources/rest/model.rest.ts b/src/app/datasources/rest/model.rest.ts
index 908ca0f..eca4221 100644
--- a/src/app/datasources/rest/model.rest.ts
+++ b/src/app/datasources/rest/model.rest.ts
@@ -16,6 +16,6 @@
}
public getResource(url: string): ng.resource.IResourceClass<ng.resource.IResource<any>> {
- return this.resource = this.$resource(`${AppConfig.apiEndpoint}${url}/:id`);
+ return this.resource = this.$resource(`${AppConfig.apiEndpoint}${url}/:id`, {id: '@id'});
}
}
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index fa3ab06..0839cc7 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -18,6 +18,8 @@
private ModelRest: IXosResourceService
) {
}
+ // FIXME if called multiple time (for different collection),
+ // it return the same observable while it should return different ones
query(model: string) {
this.loadInitialData(model);
diff --git a/src/app/views/dashboard/dashboard.html b/src/app/views/dashboard/dashboard.html
new file mode 100644
index 0000000..de4c358
--- /dev/null
+++ b/src/app/views/dashboard/dashboard.html
@@ -0,0 +1,42 @@
+<section class="content">
+ <div class="container-fluid">
+ <!--<h1>Dashboard</h1>-->
+ <div class="row">
+ <div class="col-xs-4">
+ <div class="panel panel-filled">
+ <div class="panel-body text-center">
+ <h2 class="m-b-none">
+ {{vm.nodes}}
+ <!--<span class="slight"><i class="fa fa-play fa-rotate-270 text-warning"> </i> +20%</span>-->
+ </h2>
+ <h4>Nodes</h4>
+ <!--<div class="small">% New Sessions</div>-->
+ <!--<div class="slight m-t-sm"><i class="fa fa-clock-o"> </i> Updated: <span class="c-white">10:22pm</span></div>-->
+ </div>
+ </div>
+ </div>
+
+ <div class="col-xs-4">
+ <div class="panel panel-filled">
+ <div class="panel-body text-center">
+ <h2 class="m-b-none">
+ {{vm.slices}}
+ </h2>
+ <h4>Slices</h4>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-xs-4">
+ <div class="panel panel-filled">
+ <div class="panel-body text-center">
+ <h2 class="m-b-none">
+ {{vm.instances}}
+ </h2>
+ <h4>Instances</h4>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</section>
\ No newline at end of file
diff --git a/src/app/views/dashboard/dashboard.ts b/src/app/views/dashboard/dashboard.ts
new file mode 100644
index 0000000..776acf6
--- /dev/null
+++ b/src/app/views/dashboard/dashboard.ts
@@ -0,0 +1,46 @@
+import {IModelStoreService} from '../../datasources/stores/model.store';
+import {IXosAuthService} from '../../datasources/rest/auth.rest';
+class DashboardController {
+ static $inject = ['$state', 'ModelStore', 'AuthService'];
+
+ public nodes: number;
+ public slices: number;
+ public instances: number;
+
+ constructor(
+ private $state: ng.ui.IStateService,
+ private store: IModelStoreService,
+ private auth: IXosAuthService
+ ) {
+
+ if (!this.auth.isAuthenticated()) {
+ this.$state.go('login');
+ }
+ else {
+ // this.store.query('node')
+ // .subscribe((event) => {
+ // console.log(`node`, event);
+ // this.nodes = event.length;
+ // });
+ this.store.query('slice')
+ .subscribe((event) => {
+ // console.log('slice', event);
+ this.slices = event.length;
+ });
+ // this.store.query('instance')
+ // .subscribe((event) => {
+ // console.log('isntance', event);
+ // this.instances = event.length;
+ // });
+ this.instances = 0;
+ this.nodes = 2;
+ this.slices = 3;
+ }
+ }
+}
+
+export const xosDashboard: angular.IComponentOptions = {
+ template: require('./dashboard.html'),
+ controllerAs: 'vm',
+ controller: DashboardController
+};
diff --git a/src/app/views/index.ts b/src/app/views/index.ts
index 495e2a4..ee87ef9 100644
--- a/src/app/views/index.ts
+++ b/src/app/views/index.ts
@@ -1,8 +1,10 @@
import {xosCore} from '../core/index';
import {xosCrud} from './crud/crud';
+import {xosDashboard} from './dashboard/dashboard';
export const xosViews = 'xosViews';
angular
.module('xosViews', [xosCore])
- .component('xosCrud', xosCrud);
+ .component('xosCrud', xosCrud)
+ .component('xosDashboard', xosDashboard);
diff --git a/src/index.ts b/src/index.ts
index 48244ef..4ba1f3d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -90,7 +90,20 @@
model: m.name,
related: m.relations,
xosTableCfg: {
- columns: ConfigHelpers.modeldefToTableCfg(m.fields, stateUrl)
+ columns: ConfigHelpers.modeldefToTableCfg(m.fields, stateUrl),
+ filter: 'fulltext',
+ order: {field: 'id', reverse: false}, // TODO understand dynamic interfaces
+ actions: [
+ {
+ label: 'delete',
+ icon: 'remove',
+ color: 'red',
+ cb: (item) => {
+ console.log(item);
+ item.$delete();
+ }
+ }
+ ]
},
// TODO add form config
}
diff --git a/src/interceptors.ts b/src/interceptors.ts
index 57f79f2..ef765c0 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;
diff --git a/src/routes.ts b/src/routes.ts
index e972d22..3e9274e 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -18,7 +18,7 @@
.state('xos.dashboard', {
url: '',
parent: 'xos',
- template: '<h1>Dashboard</h1>'
+ template: '<xos-dashboard></xos-dashboard>'
})
.state('xos.core', {
url: 'core',