Moved back to ng1
Change-Id: I43b284e3b3cb3ac19d43c088de988c89a7ea8807
diff --git a/src/.eslintrc.js b/src/.eslintrc.js
new file mode 100644
index 0000000..2a3da4b
--- /dev/null
+++ b/src/.eslintrc.js
@@ -0,0 +1,8 @@
+module.exports = {
+ extends: [
+ 'angular'
+ ],
+ rules: {
+ 'angular/no-service-method': 0
+ }
+}
diff --git a/src/app/components/logout/logout.component.ts b/src/app/components/logout/logout.component.ts
deleted file mode 100644
index 199cc10..0000000
--- a/src/app/components/logout/logout.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-import {Component, OnInit} from '@angular/core';
-import {Router} from '@angular/router';
-import {AuthService} from '../../services/rest/auth.service';
-
-@Component({
- selector: 'xos-logout',
- template: `
- <button *ngIf="show" (click)="logout()">Logout</button>
- `,
- providers: [AuthService],
-})
-export class LogoutComponent implements OnInit {
- public show: boolean = false;
- constructor(private AuthService: AuthService, private router: Router) {
- }
-
- ngOnInit() {
- if (this.AuthService.isAuthenticated()) {
- this.show = true;
- }
- }
-
- logout() {
- this.AuthService.logout()
- .subscribe(
- () => {
- this.router.navigate(['/login']);
- }
- );
- }
-}
-
diff --git a/src/app/components/tables/table.component.html b/src/app/components/tables/table.component.html
deleted file mode 100644
index d945ae7..0000000
--- a/src/app/components/tables/table.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<table>
- <tr>
- <th *ngFor="let col of _config.columns">{{col.label}}</th>
- </tr>
- <tr *ngFor="let item of _data">
- <td *ngFor="let col of _config.columns">
- {{item[col.prop]}}
- </td>
- </tr>
-</table>
\ No newline at end of file
diff --git a/src/app/components/tables/table.component.ts b/src/app/components/tables/table.component.ts
deleted file mode 100644
index 7e62235..0000000
--- a/src/app/components/tables/table.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Observable } from 'rxjs/Rx';
-/// <reference path="../../../../typings/index.d.ts"/>
-import { IXosTableConfig } from './../../interfaces/xos-components/table.interface';
-import {Component, OnInit, Input} from '@angular/core';
-
-@Component({
- selector: 'xos-table',
- template: require('./table.component.html'),
-})
-export class XosTableComponent implements OnInit {
-
- public _config;
- public _data;
-
- @Input() config: IXosTableConfig;
- @Input() data: Observable<any>;
-
-
- ngOnInit() {
-
- if (!this.config) {
- throw new Error('[XosTable]: You must pass a configuration');
- }
-
- this._config = this.config;
- this.data.subscribe(
- (items: any[]) => {
- this._data = items;
- }
- );
- }
-}
-
diff --git a/src/app/config/.gitignore b/src/app/config/.gitignore
deleted file mode 100644
index c96a04f..0000000
--- a/src/app/config/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
\ No newline at end of file
diff --git a/src/app/config/app.config.ts b/src/app/config/app.config.ts
new file mode 100644
index 0000000..7b6fa12
--- /dev/null
+++ b/src/app/config/app.config.ts
@@ -0,0 +1,11 @@
+/// <reference path="../../../typings/index.d.ts"/>
+
+export interface IAppConfig {
+ apiEndpoint: string;
+ websocketClient: string;
+}
+
+export const AppConfig: IAppConfig = {
+ apiEndpoint: 'http://xos.dev:3000/api',
+ websocketClient: 'http://xos.dev:3000'
+};
diff --git a/src/app/config/style.config.ts b/src/app/config/style.config.ts
new file mode 100644
index 0000000..120725f
--- /dev/null
+++ b/src/app/config/style.config.ts
@@ -0,0 +1,11 @@
+/// <reference path="../../../typings/index.d.ts"/>
+
+export interface IStyleConfig {
+ projectName: string;
+ favicon: string;
+}
+
+export const StyleConfig: IStyleConfig = {
+ projectName: 'CORD',
+ favicon: 'cord-favicon.png'
+};
diff --git a/src/app/core/footer/footer.html b/src/app/core/footer/footer.html
new file mode 100644
index 0000000..8d14479
--- /dev/null
+++ b/src/app/core/footer/footer.html
@@ -0,0 +1,6 @@
+<footer class="footer">
+ Build with ♥ by the
+ <a href="https://github.com/opencord/xos/team">
+ {{vm.brand}} Team
+ </a>
+</footer>
diff --git a/src/app/core/footer/footer.spec.ts b/src/app/core/footer/footer.spec.ts
new file mode 100644
index 0000000..06c1044
--- /dev/null
+++ b/src/app/core/footer/footer.spec.ts
@@ -0,0 +1,22 @@
+/// <reference path="../../../../typings/index.d.ts" />
+
+import * as angular from 'angular';
+import 'angular-mocks';
+import {xosFooter} from './footer';
+import {StyleConfig} from '../../config/style.config';
+
+describe('footer component', () => {
+ beforeEach(() => {
+ angular
+ .module('xosFooter', ['app/core/footer/footer.html'])
+ .component('xosFooter', xosFooter);
+ angular.mock.module('xosFooter');
+ });
+
+ it('should render "XOS Team"', angular.mock.inject(($rootScope: ng.IRootScopeService, $compile: ng.ICompileService) => {
+ const element = $compile('<xos-footer></xos-footer>')($rootScope);
+ $rootScope.$digest();
+ const footer = element.find('a');
+ expect(footer.html().trim()).toEqual(`${StyleConfig.projectName} Team`);
+ }));
+});
diff --git a/src/app/core/footer/footer.ts b/src/app/core/footer/footer.ts
new file mode 100644
index 0000000..77a223b
--- /dev/null
+++ b/src/app/core/footer/footer.ts
@@ -0,0 +1,16 @@
+import {StyleConfig} from '../../config/style.config';
+
+class FooterCtrl {
+ public brand: string;
+
+ /** @ngInject */
+ constructor() {
+ this.brand = StyleConfig.projectName;
+ }
+}
+
+export const xosFooter: angular.IComponentOptions = {
+ template: require('./footer.html'),
+ controllerAs: 'vm',
+ controller: FooterCtrl
+};
diff --git a/src/app/core/header/header.html b/src/app/core/header/header.html
new file mode 100644
index 0000000..b327754
--- /dev/null
+++ b/src/app/core/header/header.html
@@ -0,0 +1,7 @@
+<header class="header">
+ <p class="header-title">
+ <a href="#/" target="_blank">
+ {{vm.title}}
+ </a>
+ </p>
+</header>
diff --git a/src/app/core/header/header.spec.ts b/src/app/core/header/header.spec.ts
new file mode 100644
index 0000000..7c00602
--- /dev/null
+++ b/src/app/core/header/header.spec.ts
@@ -0,0 +1,22 @@
+/// <reference path="../../../../typings/index.d.ts" />
+
+import * as angular from 'angular';
+import 'angular-mocks';
+import {xosHeader} from './header';
+import {StyleConfig} from '../../config/style.config';
+
+describe('header component', () => {
+ beforeEach(() => {
+ angular
+ .module('xosHeader', ['app/core/header/header.html'])
+ .component('xosHeader', xosHeader);
+ angular.mock.module('xosHeader');
+ });
+
+ it('should render the appropriate title', angular.mock.inject(($rootScope: ng.IRootScopeService, $compile: ng.ICompileService) => {
+ const element = $compile('<xos-header></xos-header>')($rootScope);
+ $rootScope.$digest();
+ const header = element.find('a');
+ expect(header.html().trim()).toEqual(StyleConfig.projectName);
+ }));
+});
diff --git a/src/app/core/header/header.ts b/src/app/core/header/header.ts
new file mode 100644
index 0000000..c2e4c41
--- /dev/null
+++ b/src/app/core/header/header.ts
@@ -0,0 +1,15 @@
+import {StyleConfig} from '../../config/style.config';
+
+class HeaderController {
+ public title: string;
+
+ constructor() {
+ this.title = StyleConfig.projectName;
+ }
+}
+
+export const xosHeader: angular.IComponentOptions = {
+ template: require('./header.html'),
+ controllerAs: 'vm',
+ controller: HeaderController
+};
diff --git a/src/app/core/index.ts b/src/app/core/index.ts
new file mode 100644
index 0000000..d0875b6
--- /dev/null
+++ b/src/app/core/index.ts
@@ -0,0 +1,15 @@
+import {xosHeader} from './header/header';
+import {xosFooter} from './footer/footer';
+import {xosNav} from './nav/nav';
+import routesConfig from './routes';
+import {xosLogin} from './login/login';
+
+export const xosCore = 'xosCore';
+
+angular
+ .module('xosCore', ['ui.router'])
+ .config(routesConfig)
+ .component('xosHeader', xosHeader)
+ .component('xosFooter', xosFooter)
+ .component('xosNav', xosNav)
+ .component('xosLogin', xosLogin);
diff --git a/src/app/core/login/login.html b/src/app/core/login/login.html
new file mode 100644
index 0000000..f4a0724
--- /dev/null
+++ b/src/app/core/login/login.html
@@ -0,0 +1,5 @@
+<form name="login">
+ <input type="text" name="username" ng-model="username" required>
+ <input type="text" name="password" ng-model="password" required>
+ <button type="button" ng-click="vm.login(username, password)">Login</button>
+</form>
diff --git a/src/app/core/login/login.ts b/src/app/core/login/login.ts
new file mode 100644
index 0000000..af46619
--- /dev/null
+++ b/src/app/core/login/login.ts
@@ -0,0 +1,30 @@
+import {AuthService} from '../../rest/auth.rest';
+
+class LoginCtrl {
+ static $inject = ['AuthService', '$state'];
+
+ /** @ngInject */
+ constructor(
+ private authService: AuthService,
+ private $state: angular.ui.IStateService
+ ) {
+ }
+
+ public login(username: string, password: string) {
+ this.authService.login({
+ username: username,
+ password: password
+ })
+ .then(res => {
+ console.log(res);
+ this.$state.go('app');
+ })
+ .catch(e => console.error);
+ }
+}
+
+export const xosLogin: angular.IComponentOptions = {
+ template: require('./login.html'),
+ controllerAs: 'vm',
+ controller: LoginCtrl
+};
diff --git a/src/app/core/nav/nav.html b/src/app/core/nav/nav.html
new file mode 100644
index 0000000..6256369
--- /dev/null
+++ b/src/app/core/nav/nav.html
@@ -0,0 +1,7 @@
+<div class="nav">
+ <ul>
+ <li ng-repeat="route in vm.routes" ui-sref-active="active">
+ <a ui-sref="{{route.state}}">{{route.label}}</a>
+ </li>
+ </ul>
+</div>
diff --git a/src/app/core/nav/nav.scss b/src/app/core/nav/nav.scss
new file mode 100644
index 0000000..8591c15
--- /dev/null
+++ b/src/app/core/nav/nav.scss
@@ -0,0 +1,37 @@
+xos-nav {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-basis: 10%;
+ background: darken(grey, 10);
+
+ ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ background: grey;
+
+ > li {
+ display: flex;
+ flex-direction: column;
+ padding: 10px 20px;
+ border-bottom: 1px solid darken(grey, 20);
+
+ &.active {
+ background: darken(grey, 10);
+
+ > a {
+ color: #5aadbb;
+ }
+ }
+
+ &:hover {
+ background: darken(grey, 10);
+ }
+
+ > a {
+ cursor: pointer;
+ }
+ }
+ }
+}
diff --git a/src/app/core/nav/nav.ts b/src/app/core/nav/nav.ts
new file mode 100644
index 0000000..d90df10
--- /dev/null
+++ b/src/app/core/nav/nav.ts
@@ -0,0 +1,37 @@
+import './nav.scss';
+
+interface INavItem {
+ label: string;
+ state: string;
+}
+
+class NavCtrl {
+ public routes: INavItem[];
+
+ constructor() {
+ this.routes = [
+ {
+ label: 'Home',
+ state: 'xos.dashboard'
+ },
+ {
+ label: 'Instances',
+ state: 'xos.instances'
+ },
+ {
+ label: 'Slices',
+ state: 'xos.slices'
+ },
+ {
+ label: 'Nodes',
+ state: 'xos.nodes'
+ }
+ ];
+ }
+}
+
+export const xosNav: angular.IComponentOptions = {
+ template: require('./nav.html'),
+ controllerAs: 'vm',
+ controller: NavCtrl
+};
diff --git a/src/app/core/routes.ts b/src/app/core/routes.ts
new file mode 100644
index 0000000..5f37713
--- /dev/null
+++ b/src/app/core/routes.ts
@@ -0,0 +1,10 @@
+export default routesConfig;
+
+/** @ngInject */
+function routesConfig($stateProvider: angular.ui.IStateProvider, $urlRouterProvider: angular.ui.IUrlRouterProvider, $locationProvider: angular.ILocationProvider) {
+ $stateProvider
+ .state('login', {
+ url: '/login',
+ component: 'xosLogin'
+ });
+}
diff --git a/src/app/directives/protected.directive.ts b/src/app/directives/protected.directive.ts
deleted file mode 100644
index 8eac4fd..0000000
--- a/src/app/directives/protected.directive.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Created by teone on 12/5/16.
- */
-import {Directive, OnDestroy} from '@angular/core';
-import {AuthService} from '../services/rest/auth.service';
-import {Router} from '@angular/router';
-
-@Directive({
- selector: '[xosProtected]',
- providers: [AuthService]
-})
-export class ProtectedDirective implements OnDestroy {
-
- constructor(private authService: AuthService, private router: Router) {
- if (!this.authService.isAuthenticated()) {
- this.router.navigate(['/login']);
- }
- // TODO listen for logout
- }
-
- ngOnDestroy() {
- // if (this.sub != null) {
- // this.sub.unsubscribe();
- // }
- }
-}
diff --git a/src/app/hello.html b/src/app/hello.html
deleted file mode 100644
index 3ed1088..0000000
--- a/src/app/hello.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<div xos-protected></div>
-
-<h1>{{ hello }}</h1>
-
-<xos-logout></xos-logout>
-
-<xos-table [config]="cfg" [data]="data"></xos-table>
-
-<div *ngFor="let slice of slices">
- <b>{{slice.name}}</b>
- <i>{{slice.backend_status}}</i>
-</div>
diff --git a/src/app/hello.ts b/src/app/hello.ts
deleted file mode 100644
index cd90d22..0000000
--- a/src/app/hello.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Observable } from 'rxjs/Rx';
-import { IXosTableConfig } from './interfaces/xos-components/table.interface';
-/// <reference path="../../typings/index.d.ts"/>
-import {Component, OnInit} from '@angular/core';
-import {StyleConfig} from './config/style.config';
-import {ISlice} from './interfaces/models.interface';
-import {SliceStore} from './services/stores/slice.store';
-
-@Component({
- selector: 'xos-app',
- template: require('./hello.html'),
- providers: [SliceStore],
-})
-export class HelloComponent implements OnInit {
-
- // declare class properties
- public hello: string;
- public slices: ISlice[];
- public data: Observable<any>;
-
- public cfg: IXosTableConfig = {
- columns: [
- {
- label: 'Name',
- prop: 'name'
- },
- {
- label: 'Default Isolation',
- prop: 'default_isolation'
- }
- ]
- };
-
- constructor(
- private sliceStore: SliceStore
- ) {
- this.hello = `Hello ${StyleConfig.projectName}!`;
- this.slices = [];
- }
-
- ngOnInit() {
- console.log('on init');
- this.data = this.sliceStore.query();
- }
-}
diff --git a/src/app/index.ts b/src/app/index.ts
deleted file mode 100644
index 2648153..0000000
--- a/src/app/index.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { XosTableComponent } from './components/tables/table.component';
-import {NgModule} from '@angular/core';
-import {BrowserModule} from '@angular/platform-browser';
-import {HttpModule} from '@angular/http';
-import {FormsModule} from '@angular/forms';
-import {CookieService} from 'angular2-cookie/services/cookies.service';
-
-import {routing, RootComponent} from './routes';
-
-// registering components
-import {HelloComponent} from './hello';
-import {LoginComponent} from './views/login/login.component';
-import {LogoutComponent} from './components/logout/logout.component';
-
-// registering directives
-import {ProtectedDirective} from './directives/protected.directive';
-
-// registering services
-import {AuthService} from './services/rest/auth.service';
-import {XosHttp} from './services/rest/xoshttp.service';
-import {InstanceService} from './services/rest/instance.service';
-import {GlobalEvent} from './services/websockets/websocket.global';
-import {SliceService} from './services/rest/slices.service';
-
-@NgModule({
- imports: [
- BrowserModule,
- FormsModule,
- routing,
- HttpModule
- ],
- declarations: [
- RootComponent,
- HelloComponent,
- LoginComponent,
- LogoutComponent,
- XosTableComponent,
- ProtectedDirective
- ],
- providers: [
- CookieService,
- AuthService,
- XosHttp,
- InstanceService,
- SliceService,
- GlobalEvent
- ],
- bootstrap: [RootComponent]
-})
-export class AppModule {}
diff --git a/src/app/interfaces/auth.interface.ts b/src/app/interfaces/auth.interface.ts
deleted file mode 100644
index 1742fb7..0000000
--- a/src/app/interfaces/auth.interface.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export interface IAuthRequest {
- username: string;
- password: string;
-}
-
-export interface IAuthResponse {
- xossessionid: string;
- xoscsrftoken: string;
- user: string;
-}
diff --git a/src/app/interfaces/models.interface.ts b/src/app/interfaces/models.interface.ts
deleted file mode 100644
index 77c0534..0000000
--- a/src/app/interfaces/models.interface.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export interface IInstance {
- id: number;
- name: string;
- backend_status: string;
- created: string;
-}
-
-/**
- * @whatItDoes Describes the slice model.
- * @stable
- */
-export interface ISlice {
- id: number;
- name: string;
- backend_status: string;
- created: string;
-}
-
diff --git a/src/app/interfaces/ws.interface.ts b/src/app/interfaces/ws.interface.ts
deleted file mode 100644
index cadf44c..0000000
--- a/src/app/interfaces/ws.interface.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export interface IWSEvent {
- model: string;
- msg: {
- changed_fields: string[],
- object?: any,
- pk?: number
- };
-}
diff --git a/src/app/interfaces/xos-components/table.interface.ts b/src/app/interfaces/xos-components/table.interface.ts
deleted file mode 100644
index 657c89d..0000000
--- a/src/app/interfaces/xos-components/table.interface.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export interface IXosTableConfigColumn {
- label: string;
- prop: string;
-}
-
-export interface IXosTableConfigActionCallback {
- (item: string): void;
-}
-
-export interface IXosTableConfigAction {
- label: string;
- cb: IXosTableConfigActionCallback
-}
-
-export interface IXosTableConfig {
- columns: IXosTableConfigColumn[];
- actions?: IXosTableConfigAction[];
-}
\ No newline at end of file
diff --git a/src/app/main.html b/src/app/main.html
new file mode 100644
index 0000000..21ebfc2
--- /dev/null
+++ b/src/app/main.html
@@ -0,0 +1,10 @@
+<div class="main-container">
+ <xos-header></xos-header>
+ <main class="main">
+ <xos-nav></xos-nav>
+ <div class="content">
+ <div ui-view></div>
+ </div>
+ </main>
+ <xos-footer></xos-footer>
+</div>
diff --git a/src/app/main.spec.ts b/src/app/main.spec.ts
new file mode 100644
index 0000000..2409163
--- /dev/null
+++ b/src/app/main.spec.ts
@@ -0,0 +1,21 @@
+/// <reference path="../../typings/index.d.ts" />
+
+import * as angular from 'angular';
+import 'angular-mocks';
+import {main} from './main';
+
+describe('main component', () => {
+ beforeEach(() => {
+ angular
+ .module('app', ['app/main.html'])
+ .component('app', main);
+ angular.mock.module('app');
+ });
+
+ it('should render the header, title, techs and footer', angular.mock.inject(($rootScope: ng.IRootScopeService, $compile: ng.ICompileService) => {
+ const element = $compile('<app></app>')($rootScope);
+ $rootScope.$digest();
+ expect(element.find('xos-header').length).toEqual(1);
+ expect(element.find('xos-footer').length).toEqual(1);
+ }));
+});
diff --git a/src/app/main.ts b/src/app/main.ts
new file mode 100644
index 0000000..11dd35a
--- /dev/null
+++ b/src/app/main.ts
@@ -0,0 +1,3 @@
+export const main: angular.IComponentOptions = {
+ template: require('./main.html')
+};
diff --git a/src/app/rest/auth.rest.ts b/src/app/rest/auth.rest.ts
new file mode 100644
index 0000000..c72b51e
--- /dev/null
+++ b/src/app/rest/auth.rest.ts
@@ -0,0 +1,41 @@
+import {AppConfig} from '../config/app.config';
+import IHttpPromiseCallbackArg = angular.IHttpPromiseCallbackArg;
+export interface IAuthRequestData {
+ username: string;
+ password: string;
+}
+
+export interface IAuthResponseData extends IHttpPromiseCallbackArg<any> {
+ data: {
+ user: string;
+ xoscsrftoken: string;
+ xossessionid: string;
+ };
+}
+export class AuthService {
+
+
+ /** @ngInject */
+ constructor(
+ private $http: angular.IHttpService,
+ private $q: angular.IQService,
+ private $cookies: angular.cookies.ICookiesService
+ ) {
+ }
+
+ public login(data: IAuthRequestData): Promise<any> {
+ const d = this.$q.defer();
+ this.$http.post(`${AppConfig.apiEndpoint}/utility/login/`, data)
+ .then((res: IAuthResponseData) => {
+ this.$cookies.put('xoscsrftoken', res.data.xoscsrftoken);
+ this.$cookies.put('xossessionid', res.data.xossessionid);
+ this.$cookies.put('xosuser', res.data.user);
+ res.data.user = JSON.parse(res.data.user);
+ d.resolve(res.data);
+ })
+ .catch(e => {
+ d.reject(e);
+ });
+ return d.promise;
+ }
+}
diff --git a/src/app/rest/core.rest.ts b/src/app/rest/core.rest.ts
new file mode 100644
index 0000000..aace3aa
--- /dev/null
+++ b/src/app/rest/core.rest.ts
@@ -0,0 +1,18 @@
+import {AppConfig} from '../config/app.config';
+export class CoreRest {
+
+ /** @ngInject */
+ constructor(
+ private $http: angular.IHttpService,
+ private $q: angular.IQService
+ ) {
+ }
+
+ public query(): Promise<any> {
+ const d = this.$q.defer();
+ this.$http.get(`${AppConfig.apiEndpoint}/core/`)
+ .then(res => d.resolve(res.data))
+ .catch(d.reject);
+ return d.promise;
+ }
+}
diff --git a/src/app/rest/index.ts b/src/app/rest/index.ts
new file mode 100644
index 0000000..d28084b
--- /dev/null
+++ b/src/app/rest/index.ts
@@ -0,0 +1,11 @@
+import {CoreRest} from './core.rest';
+import {SlicesRest} from './slices.rest';
+import {AuthService} from './auth.rest';
+
+export const xosRest = 'xosRest';
+
+angular
+ .module('xosRest', ['ngCookies'])
+ .service('CoreRest', CoreRest)
+ .service('SlicesRest', SlicesRest)
+ .service('AuthService', AuthService);
diff --git a/src/app/rest/slices.rest.ts b/src/app/rest/slices.rest.ts
new file mode 100644
index 0000000..0d1d8a1
--- /dev/null
+++ b/src/app/rest/slices.rest.ts
@@ -0,0 +1,20 @@
+import {AppConfig} from '../config/app.config';
+
+export interface IXosResourceService {
+ getResource(): ng.resource.IResourceClass<any>;
+}
+
+export class SlicesRest implements IXosResourceService{
+ static $inject = ['$resource'];
+
+ /** @ngInject */
+ constructor(
+ private $resource: ng.resource.IResourceService
+ ) {
+
+ }
+
+ public getResource(): ng.resource.IResourceClass<ng.resource.IResource<any>> {
+ return this.$resource(`${AppConfig.apiEndpoint}/core/slices/`);
+ }
+}
diff --git a/src/app/routes.ts b/src/app/routes.ts
deleted file mode 100644
index 53fa2cc..0000000
--- a/src/app/routes.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/// <reference path="../../typings/index.d.ts"/>
-
-import {Component} from '@angular/core';
-import {RouterModule, Routes} from '@angular/router';
-import {HelloComponent} from './hello';
-import {LoginComponent} from './views/login/login.component';
-
-@Component({
- selector: 'xos-root',
- template: `
- <router-outlet></router-outlet>
- `
-})
-export class RootComponent {}
-
-export const routes: Routes = [
- {
- path: '',
- component: HelloComponent
- },
- {
- path: 'login',
- component: LoginComponent
- }
-];
-
-export const routing = RouterModule.forRoot(routes);
diff --git a/src/app/services/helpers/store.service.ts b/src/app/services/helpers/store.service.ts
deleted file mode 100644
index 80e8e05..0000000
--- a/src/app/services/helpers/store.service.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import {Injectable} from '@angular/core';
-import {IWSEvent} from '../../interfaces/ws.interface';
-import {BehaviorSubject} from 'rxjs';
-import * as _ from 'lodash';
-
-/**
- * @whatItDoes Update a BehaviorSubject after receiving an event from an Observable
- * @stable
- */
-
-@Injectable()
-export class ObservableCollectionHandler {
-
- static update(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any> {
- const collection: any[] = subject.value;
-
- const index: number = _.findIndex(collection, (i) => {
- return i.id === event.msg.object.id;
- });
- const exist: boolean = index > -1;
- const isDeleted: boolean = _.includes(event.msg.changed_fields, 'deleted');
-
- // remove
- if (exist && isDeleted) {
- _.remove(collection, {id: event.msg.object.id});
- }
- // Replace item at index using native splice
- else if (exist && !isDeleted) {
- collection.splice(index, 1, event.msg.object);
- }
- // if the element is not deleted add it
- else if (!exist && !isDeleted) {
- collection.push(event.msg.object);
- }
-
- subject.next(collection);
-
- return subject;
- }
-}
diff --git a/src/app/services/rest/auth.service.ts b/src/app/services/rest/auth.service.ts
deleted file mode 100644
index 978825f..0000000
--- a/src/app/services/rest/auth.service.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-// Imports
-import {AppConfig} from '../../config/app.config';
-import {Injectable} from '@angular/core';
-import {Http, Response, Headers} from '@angular/http';
-import {Observable} from 'rxjs/Rx';
-import {IAuthRequest, IAuthResponse} from '../../interfaces/auth.interface';
-import {CookieService} from 'angular2-cookie/core';
-
-// Import RxJs required methods
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/catch';
-
-@Injectable()
-export class AuthService {
- private xosToken: string;
- private xosSessionId: string;
- // resolve HTTP using the constructor
- constructor (private http: Http, private cookieService: CookieService) {
- }
-
- // check if the user is authenticated
- isAuthenticated(): string {
- this.xosToken = this.cookieService.get('xoscsrftoken');
- this.xosSessionId = this.cookieService.get('xossessionid');
- return this.xosToken && this.xosSessionId;
- }
-
- // get auth info to authenticate API
- getUserHeaders(): Headers {
- const headers = new Headers();
- headers.append('x-csrftoken', this.cookieService.get('xoscsrftoken'));
- headers.append('x-sessionid', this.cookieService.get('xossessionid'));
- return headers;
- }
-
- // log the user in
- login(auth: IAuthRequest): Observable<IAuthResponse> {
- return this.http.post(`${AppConfig.apiEndpoint}/utility/login/`, auth)
- .map((res: Response) => res.json())
- .map((auth: IAuthResponse) => {
- this.storeAuth(auth);
- auth.user = JSON.parse(auth.user);
- return auth;
- })
- .catch((error: any) => Observable.throw(error.json().error || 'Server error'));
- }
-
- // logout the user
- logout(): Observable<any> {
- return this.http.post(`${AppConfig.apiEndpoint}/utility/logout/`, {xossessionid: this.xosSessionId})
- .map((res: Response) => {
- this.removeAuth();
- return res.text();
- })
- .catch((error: any) => Observable.throw(error.json().error || 'Server error'));
- }
-
- // save cookies
- private storeAuth(auth: IAuthResponse): void {
- this.cookieService.put('xoscsrftoken', auth.xoscsrftoken);
- this.cookieService.put('xossessionid', auth.xossessionid);
- }
-
- // remove cookies
- private removeAuth(): void {
- this.cookieService.remove('xoscsrftoken');
- this.cookieService.remove('xossessionid');
- }
-}
-
diff --git a/src/app/services/rest/core.service.ts b/src/app/services/rest/core.service.ts
deleted file mode 100644
index a283024..0000000
--- a/src/app/services/rest/core.service.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-// Imports
-import {AppConfig} from '../../config/app.config';
-import {Injectable} from '@angular/core';
-import {Response} from '@angular/http';
-import {Observable} from 'rxjs/Rx';
-import {XosHttp} from './xoshttp.service';
-
-// Import RxJs required methods
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/catch';
-
-@Injectable()
-export class CoreService {
- // private instance variable to hold base url
- private baseUrl = AppConfig.apiEndpoint;
-
- // Resolve HTTP using the constructor
- constructor (private http: XosHttp) {}
-
- // Fetch all existing comments
- getCoreEndpoints(): Observable<any[]> {
-
- const search = 'some=param';
-
- // ...using get request
- return this.http.get(`${this.baseUrl}/core/`, {search})
- // ...and calling .json() on the response to return data
- .map((res: Response) => res.json())
- // ...errors if any
- .catch((error: any) => Observable.throw(error.json().error || 'Server error'));
-
- }
-}
diff --git a/src/app/services/rest/instance.service.ts b/src/app/services/rest/instance.service.ts
deleted file mode 100644
index 899b484..0000000
--- a/src/app/services/rest/instance.service.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-// Imports
-import {AppConfig} from '../../config/app.config';
-import {AuthService} from './auth.service';
-import {Injectable} from '@angular/core';
-import {Response} from '@angular/http';
-import {XosHttp} from './xoshttp.service';
-import {Observable} from 'rxjs/Rx';
-
-// Import RxJs required methods
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/catch';
-
-@Injectable()
-export class InstanceService {
- private baseUrl = AppConfig.apiEndpoint;
- constructor (private http: XosHttp, private authService: AuthService) {}
- // Fetch all existing instances
- query() : Observable<any[]> {
- return this.http.get(`${this.baseUrl}/core/instances/`)
- .map((res: Response) => res.json())
- .catch((error: any) => Observable.throw(error.response.json().error || 'Server error'));
- }
-}
diff --git a/src/app/services/rest/slices.service.ts b/src/app/services/rest/slices.service.ts
deleted file mode 100644
index 2b71d7a..0000000
--- a/src/app/services/rest/slices.service.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-// Imports
-import {AppConfig} from '../../config/app.config';
-import { Injectable } from '@angular/core';
-import { Response} from '@angular/http';
-import {XosHttp} from './xoshttp.service';
-import {Observable} from 'rxjs/Rx';
-
-// Import RxJs required methods
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/catch';
-
-@Injectable()
-export class SliceService {
- private baseUrl = AppConfig.apiEndpoint;
- constructor (private http: XosHttp) {}
- // Fetch all existing instances
- query() : Observable<any[]> {
- return this.http.get(`${this.baseUrl}/core/slices/`)
- .map((res: Response) => res.json())
- .catch((error: any) => Observable.throw(error.response.json().error || 'Server error'));
- }
-}
diff --git a/src/app/services/rest/xoshttp.service.ts b/src/app/services/rest/xoshttp.service.ts
deleted file mode 100644
index a300ee3..0000000
--- a/src/app/services/rest/xoshttp.service.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import {Http, RequestOptionsArgs, Response, URLSearchParams} from '@angular/http';
-import {Injectable} from '@angular/core';
-import {Observable} from 'rxjs';
-import {AuthService} from './auth.service';
-/**
- * Created by teone on 12/6/16.
- */
-@Injectable()
-export class XosHttp {
- constructor(
- private http: Http,
- private authService: AuthService
- ) {
- }
-
- // TODO intercept non authenticated calls and send to login (remove cookies)
- // TODO add POST, PUT, DELETE declaration
- get(url: string, options?: RequestOptionsArgs): Observable<Response> {
-
- options = this.checkOptions(options);
- options = this.getHeaders(options);
- options = this.getParams(options);
-
- return this.http.get(url, options);
- }
-
- private checkOptions(options?: RequestOptionsArgs): RequestOptionsArgs {
- // if options are not there, create them
- if (!options) {
- options = {};
- }
- return options;
- }
-
- private getHeaders(options: RequestOptionsArgs): RequestOptionsArgs {
- // add auth headers
- options.headers = this.authService.getUserHeaders();
- return options;
- }
-
- private getParams(options: RequestOptionsArgs): RequestOptionsArgs {
- // add the no_hyperlinks param
- if (!options.search) {
- options.search = new URLSearchParams();
- }
-
- if (options.search instanceof URLSearchParams) {
- options.search.set('no_hyperlinks', '1');
- }
- else if (typeof options.search === 'string') {
- options.search += '&no_hyperlinks=1';
- }
- return options;
- }
-}
diff --git a/src/app/services/stores/instance.store.ts b/src/app/services/stores/instance.store.ts
deleted file mode 100644
index 9a07095..0000000
--- a/src/app/services/stores/instance.store.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-import {Injectable} from '@angular/core';
-import {BehaviorSubject} from 'rxjs/Rx';
-import {IInstance} from '../../interfaces/models.interface';
-import {InstanceService} from '../rest/instance.service';
-import {IWSEvent} from '../../interfaces/ws.interface';
-import {GlobalEvent} from '../websockets/websocket.global';
-import {ObservableCollectionHandler} from '../helpers/store.service';
-
-@Injectable()
-export class InstanceStore {
- private _instances: BehaviorSubject<IInstance[]> = new BehaviorSubject([]);
- constructor(private instanceService: InstanceService, private globalEvent: GlobalEvent) {
- this.loadInitialData();
- this.globalEvent.list()
- .filter((e: IWSEvent) => e.model === 'Instance')
- .subscribe(
- (event: IWSEvent) => {
- ObservableCollectionHandler.update(event, this._instances);
- }
- );
- }
-
- loadInitialData() {
- this.instanceService.query()
- .subscribe(
- res => {
- this._instances.next(res);
- },
- err => console.log('Error retrieving Instances', err)
- );
- }
-
- query() {
- return this._instances.asObservable();
- }
-
-}
diff --git a/src/app/services/stores/slice.store.ts b/src/app/services/stores/slice.store.ts
deleted file mode 100644
index aeab9f3..0000000
--- a/src/app/services/stores/slice.store.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-import {Injectable} from '@angular/core';
-import {BehaviorSubject} from 'rxjs/Rx';
-import {ISlice} from '../../interfaces/models.interface';
-import {IWSEvent} from '../../interfaces/ws.interface';
-import {GlobalEvent} from '../websockets/websocket.global';
-import {SliceService} from '../rest/slices.service';
-import {ObservableCollectionHandler} from '../helpers/store.service';
-
-@Injectable()
-export class SliceStore {
- private _slices: BehaviorSubject<ISlice[]> = new BehaviorSubject([]);
- constructor(private sliceService: SliceService, private globalEvent: GlobalEvent) {
- this.loadInitialData();
- this.globalEvent.list()
- .filter((e: IWSEvent) => e.model === 'Slice')
- .subscribe(
- (event: IWSEvent) => {
- ObservableCollectionHandler.update(event, this._slices);
- }
- );
- }
-
- loadInitialData() {
- this.sliceService.query()
- .subscribe(
- res => {
- this._slices.next(res);
- },
- err => console.log('Error retrieving Instances', err)
- );
- }
-
- query() {
- return this._slices.asObservable();
- }
-
-}
diff --git a/src/app/services/websockets/websocket.global.ts b/src/app/services/websockets/websocket.global.ts
deleted file mode 100644
index 46bfbe1..0000000
--- a/src/app/services/websockets/websocket.global.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-
-import {Injectable} from '@angular/core';
-import {Subject} from 'rxjs/Rx';
-import * as io from 'socket.io-client';
-import {AppConfig} from '../../config/app.config';
-import {IWSEvent} from '../../interfaces/ws.interface';
-
-@Injectable()
-export class GlobalEvent {
- private _events: Subject<IWSEvent> = new Subject<IWSEvent>();
- private socket;
- constructor() {
- this.socket = io(AppConfig.websocketClient);
- this.socket.on('event', (data: IWSEvent): void => {
- console.log('event', data);
- this._events.next(data);
- });
- }
- list() {
- return this._events.asObservable();
- }
-}
diff --git a/src/app/views/login/login.component.ts b/src/app/views/login/login.component.ts
deleted file mode 100644
index 96492e3..0000000
--- a/src/app/views/login/login.component.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/// <reference path="../../../../typings/index.d.ts"/>
-import {Component} from '@angular/core';
-import {Router} from '@angular/router';
-import {IAuthRequest, IAuthResponse} from '../../interfaces/auth.interface';
-import {StyleConfig} from '../../config/style.config';
-import {AuthService} from '../../services/rest/auth.service';
-
-export class Auth {
- constructor(
- public username: string,
- public password: string
- ) {
- }
-}
-
-@Component({
- selector: 'xos-login',
- template: require('./login.html'),
- providers: [AuthService],
-})
-export class LoginComponent {
- public auth: IAuthRequest;
- public brandName;
- constructor(private AuthService: AuthService, private router: Router) {
- this.auth = new Auth('', '');
- this.brandName = StyleConfig.projectName;
- }
-
- onSubmit(auth: IAuthRequest) {
- this.AuthService.login(auth)
- .subscribe(
- (user: IAuthResponse) => {
- this.router.navigate(['/']);
- }
- );
- }
-}
-
diff --git a/src/app/views/login/login.html b/src/app/views/login/login.html
deleted file mode 100644
index 0f8362c..0000000
--- a/src/app/views/login/login.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="login-container">
- <div class="login-form">
- <h2>Login to {{brandName}}</h2>
- <form (ngSubmit)="onSubmit(loginForm.value)" #loginForm="ngForm">
- <input type="email" [(ngModel)]="auth.username" name="username" required/>
- <input type="password" [(ngModel)]="auth.password" name="password" required/>
- <button type="submit" [disabled]="!loginForm.form.valid">Login</button>
- </form>
- </div>
-</div>
diff --git a/src/images/cord-favicon.png b/src/images/cord-favicon.png
deleted file mode 100644
index 758902e..0000000
--- a/src/images/cord-favicon.png
+++ /dev/null
Binary files differ
diff --git a/src/images/opencloud-favicon.png b/src/images/opencloud-favicon.png
deleted file mode 100644
index d49afe0..0000000
--- a/src/images/opencloud-favicon.png
+++ /dev/null
Binary files differ
diff --git a/src/index.html b/src/index.html
index 6d6e8c1..591a249 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3,13 +3,13 @@
<head>
<base href="/">
<meta charset="utf-8">
- <title>XOS</title>
+ <title>FountainJS</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
- <link rel="icon" type="image/png" href="images/cord-favicon.png" />
+ <link rel="icon" type="image/png" href="http://fountainjs.io/assets/imgs/fountain.png" />
</head>
- <body>
- <xos-root>Loading XOS...</xos-root>
+ <body ng-app="app">
+ <ui-view></ui-view>
</body>
</html>
diff --git a/src/index.scss b/src/index.scss
index f1df96a..2cbe173 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -1,3 +1,65 @@
-body {
- background-color: lightgrey;
+
+
+html, body, #root {
+ margin: 0;
+ height: 100%;
+ font-family: 'Open Sans', sans-serif;
+}
+
+body, h1, h2, h3 {
+ font-weight: 300
+}
+
+a {
+ color: white;
+ text-decoration: none;
+
+ &:hover {
+ color: #5aadbb;
+ }
+}
+
+.main-container {
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+}
+.main {
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+
+ .content {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ flex-basis: 90%;
+ background: darken(grey, 25);
+ padding: 20px;
+ }
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ background-color: #1f1f1f;
+}
+.header-title {
+ flex: 1;
+ font-size: 1.5rem;
+ margin: 1rem;
+}
+.header-date {
+ flex: 1;
+ text-align: right;
+ margin: 1rem;
+ white: white;
+}
+
+.footer {
+ padding: 0.5rem;
+ font-size: 1rem;
+ background-color: #1f1f1f;
+ text-align: center;
+ color: white;
}
diff --git a/src/index.spec.js b/src/index.spec.js
index 4a2f55b..8c19f76 100644
--- a/src/index.spec.js
+++ b/src/index.spec.js
@@ -1,46 +1,4 @@
-Error.stackTraceLimit = Infinity;
-require('core-js/client/shim');
-
-require('@angular/common');
-require('rxjs');
-
-require('zone.js/dist/zone');
-require('zone.js/dist/long-stack-trace-zone');
-require('zone.js/dist/proxy');
-require('zone.js/dist/sync-test');
-require('zone.js/dist/jasmine-patch');
-require('zone.js/dist/async-test');
-require('zone.js/dist/fake-async-test');
-require('jquery');
-
-// loading app files
const context = require.context('./app', true, /\.(js|ts|tsx)$/);
-context.keys().forEach(context);
-
-// use this for debug
-// context.keys().forEach(function(path) {
-// try {
-// context(path);
-// } catch(err) {
-// console.error('[ERROR] WITH SPEC FILE: ', path);
-// console.error(err);
-// }
-// });
-
-// loading specs
-const specFiles = require.context('../spec', true, /\.(js|ts|tsx)$/);
-specFiles.keys().forEach(specFiles);
-// use this for debug
-// specFiles.keys().forEach(function(path) {
-// try {
-// specFiles(path);
-// } catch(err) {
-// console.error('[ERROR] WITH SPEC FILE: ', path);
-// console.error(err);
-// }
-// });
-
-const testing = require('@angular/core/testing');
-const testingBrowser = require('@angular/platform-browser-dynamic/testing');
-
-testing.TestBed.initTestEnvironment(testingBrowser.BrowserDynamicTestingModule, testingBrowser.platformBrowserDynamicTesting());
+context.keys().forEach(function(f) {
+ context(f);
+});
diff --git a/src/index.ts b/src/index.ts
index baed6f9..66a32f1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,24 +1,25 @@
-/// <reference path="../typings/index.d.ts"/>
+/// <reference path="../typings/index.d.ts" />
-import 'core-js/client/shim';
-import 'zone.js/dist/zone';
+import * as angular from 'angular';
-import '@angular/common';
-import 'rxjs';
+import 'angular-ui-router';
+import 'angular-resource';
+import 'angular-cookies';
+import routesConfig from './routes';
+
+import {main} from './app/main';
import './index.scss';
+import {xosCore} from './app/core/index';
+import {xosRest} from './app/rest/index';
+import {xosViews} from './app/views/index';
+import {interceptorConfig, userStatusInterceptor, CredentialsInterceptor} from './interceptors';
-import {enableProdMode} from '@angular/core';
-import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
-import {AppModule} from './app';
+angular
+ .module('app', [xosCore, xosRest, xosViews, 'ui.router', 'ngResource'])
+ .config(routesConfig)
+ .config(interceptorConfig)
+ .factory('UserStatusInterceptor', userStatusInterceptor)
+ .factory('CredentialsInterceptor', CredentialsInterceptor)
+ .component('xos', main);
-declare var process: any;
-
-if (process.env.NODE_ENV === 'production') {
- enableProdMode();
-} else {
- Error['stackTraceLimit'] = Infinity; // tslint:disable-line:no-string-literal
- require('zone.js/dist/long-stack-trace-zone'); // tslint:disable-line:no-var-requires
-}
-
-platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/src/interceptors.ts b/src/interceptors.ts
new file mode 100644
index 0000000..bd4cba3
--- /dev/null
+++ b/src/interceptors.ts
@@ -0,0 +1,39 @@
+/// <reference path="../typings/index.d.ts" />
+
+export function interceptorConfig($httpProvider: angular.IHttpProvider, $resourceProvider: angular.resource.IResourceServiceProvider) {
+ $httpProvider.interceptors.push('UserStatusInterceptor');
+ $httpProvider.interceptors.push('CredentialsInterceptor');
+ $resourceProvider.defaults.stripTrailingSlashes = false;
+}
+
+export function userStatusInterceptor($state: angular.ui.IStateService) {
+
+ const checkLogin = (res) => {
+ if (res.status === 403) {
+ $state.go('login');
+ }
+ else if (res.data.status === 403) {
+ $state.go('login');
+ }
+ return res;
+ };
+
+ return {
+ response: checkLogin,
+ responseError: checkLogin
+ };
+}
+
+export function CredentialsInterceptor($cookies: angular.cookies.ICookiesService) {
+ return {
+ request: (req) => {
+ 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-sessionid'] = $cookies.get('xossessionid');
+ return req;
+ }
+ };
+}
diff --git a/src/routes.ts b/src/routes.ts
new file mode 100644
index 0000000..3c8ae3d
--- /dev/null
+++ b/src/routes.ts
@@ -0,0 +1,43 @@
+/// <reference path="../typings/index.d.ts" />
+
+export default routesConfig;
+
+/** @ngInject */
+function routesConfig($stateProvider: angular.ui.IStateProvider, $urlRouterProvider: angular.ui.IUrlRouterProvider, $locationProvider: angular.ILocationProvider) {
+ $locationProvider.html5Mode(false).hashPrefix('');
+ $urlRouterProvider.otherwise('/');
+
+ $stateProvider
+ .state('xos', {
+ abstract: true,
+ url: '/',
+ component: 'xos'
+ })
+ .state('xos.dashboard', {
+ url: '',
+ parent: 'xos',
+ template: '<h1>Dashboard</h1>'
+ })
+ .state('xos.instances', {
+ url: 'instances',
+ parent: 'xos',
+ template: '<h1>Instances</h1>'
+ })
+ .state('xos.slices', {
+ url: 'slices',
+ parent: 'xos',
+ component: `xosCrud`,
+ data: {
+ title: 'Slices',
+ resource: 'SlicesRest',
+ xosTableCfg: {
+ columns: ['name', 'default_isolation']
+ }
+ }
+ })
+ .state('xos.nodes', {
+ url: 'nodes',
+ parent: 'xos',
+ template: '<h1>Nodes</h1>'
+ });
+}