Merge "[CORD-2188] Multi-stage build for xos-gui"
diff --git a/docs/developer/gui_extensions.md b/docs/developer/gui_extensions.md
index b299283..52bf7f7 100644
--- a/docs/developer/gui_extensions.md
+++ b/docs/developer/gui_extensions.md
@@ -1,23 +1,74 @@
-# Create a custom GUI Extension
+# Creating a custom GUI Extension
The CORD GUI is designed to be extensible. There are two cases in which we envision an extension to be needed:
- Provide a different view over data
- Create custom interfaces for services
-The development process for the two cases it's absolutely the same.
+The development process for both is the same.
-We suggest to get started by duplicating [xos-sample-gui-extension](https://github.com/opencord/xos-sample-gui-extension)
-
-## Development
+## Starting Development
+
+### Option 1: Use the provided Yeoman xos-gui-extension generator
+
+The provided generator in `generator-xos-gui-extension` will generate a new GUI extension for you with the name of
+your choice and demo components based on a simplified `sample-gui-extension`. No refactoring necessary.
+
+#### Prerequisites
+You must have the Yeoman toolset installed via npm on your system. It can be installed by running `npm install --global yo`.
+If you encounter any issues, full detailed instructions on installation can be found at the [Yeoman website](http://yeoman.io/codelab/setup.html).
+
+#### Installation
+Once you have successfully installed Yeoman, run the following to install the generator.
+```bash
+cd ~cord/orchestration/xos-gui/generator-xos-gui-extension
+npm link
+```
+To run the generator, simply run `yo xos-gui-extension` from whatever location in your file system you wish to place your
+new GUI extension. The extension will prompt for a name for your extension.
+
+
+### Option 2: Copy over sample-gui-extension
+If you choose not to use the Yeoman generator, you can copy over the contents of `sample-gui-extension` to your desired
+destination in your file system. If you are creating a GUI extension to used with a service, we suggest creating the
+extension in a folder named `gui` in the service's `xos` folder as follows: `orchestration/xos_services/service_name/xos/gui/`.
+When changing the name of `sample-gui-extension`, you must be wary to change all instances of `sample-gui-extension` in the
+extension folder.
+
-The development environment is the same as `xos-gui` so you can use the `npm start` command and augment it with the same sets fo environment variables.
-The dev server is going to proxy your requests to the appropriate backend and load the base application from it.
+## Adding an extension to the build system
-## Add an extension to a profile
+To deploy your GUI extension with a cord profile you'll need to reference it in `platform-install` and `build`.
+The following steps must be followed to ensure that your GUI extension builds correctly with XOS.
+
+### Adding the extension to `docker_images.yml`
+
+Open `cord/build/docker_images.yml`. Locate the section of the file with other GUI extensions, and add the following:
+
+```yaml
+- name: xosproject/gui-extension-sample
+ repo: sampleRepo # should match Gerrit repo name
+ path: "xos/gui" # path to find extension in the repo (i.e. sampleRepo/xos/gui/)
+```
+Please maintain ascending alphabetical order among the GUI extensions when inserting your own extension.
+
+### Adding the extension to the podconfig scenario
+
+Open the `config.yml` file of the podconfig scenario relevant to your GUI extension (e.g. cord, mock, local).
+Locate the section titled `docker_image_whitelist` and add your GUI extension.
+
+```yaml
+docker_image_whitelist:
+# ...other docker images...
+ - "xosproject/gui-extension-rcord"
+ - "xosproject/gui-extension-sample" # extension added in alphabetical order
+ - "xosproject/gui-extension-vtr"
+# ...more docker images...
+
+```
+
+### Adding the extension to the podconfig profile manifest
-To deploy your GUI extension with a cord profile you'll need to reference it in `platform-install`.
-
-Open the `profile-manifest` you're working on (eg: profile_manifests/frontend.yml) and locate `enabled_gui_extensions`.
+Open the `profile-manifest` relevant to the podconfig you're working on (eg: profile_manifests/frontend.yml) and locate `enabled_gui_extensions`.
It may appear in two forms, depending whether or not there are others loaded extensions:
```yaml
enabled_gui_extensions:
@@ -28,7 +79,9 @@
```yaml
enabled_gui_extensions: []
```
-_NOTE: if it is not there, just create it_
+_NOTE: if it is not there, just create it._
+
+### Change conf export settings to match extension name
To add your extension, just add it to the list:
```yaml
@@ -40,7 +93,7 @@
```
_NOTE: if it was defined as an empty array you'll need to remove the square brackets (`[]`)_
-The `name` field must match the directory in which the GUI Extension is built. You can update it in `conf/gulp.conf.js`, just locate:
+You must make sure that the `name` field matches the directory in which the GUI Extension is built. You can update it in `conf/gulp.conf.js`.
```js
exports.paths = {
src: 'src',
@@ -51,6 +104,97 @@
tasks: 'gulp_tasks'
};
```
-and replace `sample` with the appropriate name.
+and replace `sample` with your appropriate name. If you used the Yeoman generator, `sample` will already have been
+replaced with the GUI extension name you chose.
-The `path` field identify the directory (starting from the `repo` root), in which your extension is stored. As now is not supported the loading from external sources.
\ No newline at end of file
+The `path` field identifies the directory (starting from the CORD `repo` root), in which your extension is stored.
+Loading from external sources is not currently supported.
+
+## Additional Tips
+
+### Including Extra Files
+
+Additional necessary files (such as stylesheets or config files) can be added to the profile manifest as follows,
+with the extension's `src` folder as the root. Here, we use `xos-sample-gui-extension` as an example.
+
+```yaml
+enabled_gui_extensions:
+ - name: sample
+ path: orchestration/xos-sample-gui-extension
+ extra_files:
+ - app/style/style.css
+```
+
+### Generating config files
+
+During development, you may find it necessary to create separate config files in order to include other files used in
+your extension (such as images). The path to your extension may vary depending on whether you are running it locally
+(`./xos/extensions/extension-name`) vs. on a container in production (`./extensions/extension-name`).
+
+You can create separate `customconfig.local.js` and `customconfig.production.js` files in the `conf/` folder, and then edit the
+following portion of the appropriate `webpack.conf.js` file as follows:
+
+```js
+new CopyWebpackPlugin([
+ { from: `./conf/app/app.config.${env}.js`, to: `app.config.js` },
+ { from: `./conf/app/style.config.${brand}.js`, to: `style.config.js` },
+ // add your file here
+ { from: `./conf/app/customconfig.local.js`, to: `customconfig.js`}
+ ])
+```
+
+`webpack.conf.js` will be used in a local development environment, such as when running `npm start`
+
+`webpack-dist.conf.js` will be used in a production container after deploying a profile.
+
+### Handy XOS Components and Services
+
+The following XOS components and services may be helpful to you in your GUI extension development.
+
+#### XosComponentInjector
+Allows for the injection of components into the XOS GUI by specifying a target element ID. Useful IDs include:
+* `#dashboard-component-container`: the dashboard as seen on the XOS home
+* `#side-panel-container`: a side panel that can slide out from the right. However, there is also a `XosSidePanel`
+service that can make development easier.
+
+#### XosConfirm
+Allows for the creation of confirmation modal dialogs to confirm whether or not to execute a selected action.
+
+#### XosKeyboardShortcut
+Allows for the creation of custom user keyboard shortcuts. See the provided `components/demo.ts` as an example.
+
+#### XosModelStore
+Provides easy access to model ngResources provided by an XOS service. Can be used as follows in your component's
+associated TypeScript file:
+
+```typescript
+import {Subscription} from 'rxjs/Subscription';
+export class ExampleComponent {
+ static $inject = ['XosModelStore'];
+ public resource;
+ private modelSubscription : Subscription;
+ constructor(
+ private XosModelStore: any,
+ ){}
+
+ $onInit() {
+ this.modelSubscription = this.XosModelStore.query('SampleModel', '/sampleservice/SampleModels').subscribe(
+ res => {
+ this.resource = res;
+ }
+ );
+ }
+}
+export const exampleComponent : angular.IComponentOptions = {
+ template: require('./example.component.html'),
+ controllerAs: 'vm',
+ controller: ExampleComponent
+}
+```
+
+#### XosNavigationService
+Used to create custom navigation links in the left navigation panel.
+
+#### XosSidePanel
+Makes the injection of a custom side panel somewhat easier (no need to specify a target)
+
diff --git a/docs/developer/quickstart.md b/docs/developer/quickstart.md
index c0b05a0..a27c800 100644
--- a/docs/developer/quickstart.md
+++ b/docs/developer/quickstart.md
@@ -1,41 +1,42 @@
# GUI Quickstart
-_We assume that you already have the code downloaded with `repo`_
+_We assume that you already have the CORD source downloaded with `repo`_
-## Setting up the `frontend` configuration
+## Setting up a podconfig
-The easiest to work on the XOS GUI is to create a local deployment of the `frontend` configuration of CORD.
+For front-end development, using a podconfig with your choice of CORD flavors and the `mock` or `local` scenario is sufficient.
+`mock` will create a vagrant VM running all the necessary containers, while `local` directly creates the containers on your machine,
+so we recommend using `mock` when developing on your own laptop/desktop.
-You can find detailed instructions in the `platform-install` documentation, but for all that we need you can:
+Assuming you have already downloaded and ran the `cord-bootstrap.sh` script, setup is easy:
+
+```bash
+cd ~/cord/build/
+make PODCONFIG={CORD_FLAVOR}-mock.yml config
+make build |& tee ~/build.out
+```
+
+Further details on the mock configuration and its setup can be found at [Mock Configuration Workflow](/xos/dev/workflow_mock_single.md)
+
+### Login credentials
+After the mock profile finishes building, you can find the credentials necessary to login into XOS by running the following:
```
-cd opencord/build/platform-install
-vagrant up head-node
-vagrant ssh
-// you're now inside the vagrant VM
-cd cord/buid/platform-install
-ansible-playbook -i inventory/frontend deploy-xos-playbook.yml
-```
-
-
-> This commands will spin up a local vm and deploy the minimal configuration of CORD in it. <br/>
-> _NOTE the first you'll execute this commands they may take a long time (~20 min)_
-
-#### Retrieve the password to access XOS
-```
-cat ~/cord/buid/platform-install/credentials/xosadmin@opencord.org
+cat ~/cord/build/platform-install/credentials/xosadmin@opencord.org
// save the output somewhere
```
## Serving the GUI in development mode
-Once your basic CORD config is up and running you should be able to access the GUI at `http://192.168.46.100/xos/`
-but that's not your development copy, it is the one deployed inside a container in XOS.
+Once your basic CORD config is up and running you should be able to access the GUI at `http://192.168.46.100/xos/`.
+
+NOTE: This is not your development copy, it is the one deployed inside a container in XOS and will not change until
+the container is torn down and redeployed.
To launch a development copy:
```
// back to your local system
-cd opencord/orchestration/xos-gui
+cd cord/orchestration/xos-gui
npm install
npm start
```
@@ -45,9 +46,9 @@
**Now you're ready to start working on it!**
To get start, login using username `xosadmin@opencord.org` and the password you previously saved,
-then pick any file and make a change, you'll see the GUI reload.
+then pick any file and make a change, and you'll see the GUI reload.
-### Configuring the `dev` GUI
+## Configuring the `dev` GUI
There are two configuration file available in the application, and they depend on the environment. You can find the various possibilities in `conf/app`, and they regard application constants, such as `apiEndpoint`, or branding elements, such as `projectName`.
@@ -55,6 +56,8 @@
- `PROXY`: you can use this variable to send request to an arbitrary XOS installation (eg: `clnode022.clemson.cloudlab.us:8080`)
- `BRAND`: to configure style constants (eg: `cord`, `opencloud`)
+## Working with an existing XOS installation
+
You can also specify a different installation of XOS as backend by using the `PROXY` environment variable.
-For example you can connect to a remote CORD-in-a-box installation for debugging purposes with:
+For example, you can connect to a remote CORD-in-a-box installation for debugging purposes with:
`PROXY=ms1106.utah.cloudlab.us:8080 npm start`
diff --git a/generator-xos-gui-extension/README.md b/generator-xos-gui-extension/README.md
new file mode 100644
index 0000000..fb671d7
--- /dev/null
+++ b/generator-xos-gui-extension/README.md
@@ -0,0 +1,17 @@
+#generator-xos-gui-extension
+
+## About
+This is a Yeoman generator intended to aid with the development of new XOS GUI Extensions. It generates a new GUI extension
+with only one component.
+
+## Installation
+Run the following command in this directory:
+```bash
+npm link
+```
+
+## Running the generator
+Run the following command in any directory and follow the prompts.
+```bash
+yo xos-gui-extension
+```
\ No newline at end of file
diff --git a/generator-xos-gui-extension/generators/README.md b/generator-xos-gui-extension/generators/README.md
deleted file mode 100644
index c13ead2..0000000
--- a/generator-xos-gui-extension/generators/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-#generator-xos-gui-extension
-
-## About
-This is a Yeoman generator intended to aid with the development of new XOS GUI Extensions. It generates a new GUI extension
-with only one component.
-
-## Installation
-Run the following command in this directory and follow the prompts.
-```
-yo xos-gui-extension
-```
\ No newline at end of file
diff --git a/generator-xos-gui-extension/generators/templates/conf/webpack-dist.conf.js b/generator-xos-gui-extension/generators/templates/conf/webpack-dist.conf.js
index c870043..5f924fb 100755
--- a/generator-xos-gui-extension/generators/templates/conf/webpack-dist.conf.js
+++ b/generator-xos-gui-extension/generators/templates/conf/webpack-dist.conf.js
@@ -105,7 +105,10 @@
'.web.js',
'.js',
'.ts'
- ]
+ ],
+ alias: {
+ "ngprogress": path.resolve(__dirname, '../node_modules/ngprogress/build/ngProgress.js')
+ }
},
entry: {
app: `./${conf.path.src('index')}`,
diff --git a/generator-xos-gui-extension/generators/templates/conf/webpack-test.conf.js b/generator-xos-gui-extension/generators/templates/conf/webpack-test.conf.js
index c0115ab..04f9e4c 100755
--- a/generator-xos-gui-extension/generators/templates/conf/webpack-test.conf.js
+++ b/generator-xos-gui-extension/generators/templates/conf/webpack-test.conf.js
@@ -73,7 +73,10 @@
'.web.js',
'.js',
'.ts'
- ]
+ ],
+ alias: {
+ "ngprogress": path.resolve(__dirname, '../node_modules/ngprogress/build/ngProgress.js')
+ }
},
ts: {
configFileName: 'tsconfig.json'
diff --git a/generator-xos-gui-extension/generators/templates/conf/webpack.conf.js b/generator-xos-gui-extension/generators/templates/conf/webpack.conf.js
index d70e771..f0dbc51 100755
--- a/generator-xos-gui-extension/generators/templates/conf/webpack.conf.js
+++ b/generator-xos-gui-extension/generators/templates/conf/webpack.conf.js
@@ -99,7 +99,10 @@
'.web.js',
'.js',
'.ts'
- ]
+ ],
+ alias: {
+ "ngprogress": path.resolve(__dirname, '../node_modules/ngprogress/build/ngProgress.js')
+ }
},
entry: `./${conf.path.src('index')}`,
ts: {
diff --git a/generator-xos-gui-extension/generators/templates/package.json b/generator-xos-gui-extension/generators/templates/package.json
index 32a0676..b56f74e 100644
--- a/generator-xos-gui-extension/generators/templates/package.json
+++ b/generator-xos-gui-extension/generators/templates/package.json
@@ -1,5 +1,5 @@
{
- "version": "3.0.0",
+ "version": "4.0.0",
"dependencies": {
"angular": "1.6.3",
"angular-animate": "1.6.3",
diff --git a/package.json b/package.json
index cb93442..d9bd9b9 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,8 @@
"dependencies": {
"angular": "1.6.3",
"angular-animate": "1.6.3",
+ "angular-base64": "^2.0.5",
+ "angular-chart.js": "^1.1.1",
"angular-cookies": "1.6.3",
"angular-resource": "1.6.3",
"angular-toastr": "2.1.1",
@@ -15,6 +17,7 @@
"lodash": "4.17.4",
"ngprogress": "1.1.1",
"oclazyload": "1.1.0",
+ "moment": "^2.19.0",
"pluralize": "3.1.0",
"rxjs": "5.2.0",
"socket.io-client": "1.7.3"
diff --git a/src/app/core/debug/debug-model.spec.ts b/src/app/core/debug/debug-model.spec.ts
index 46b6344..b081de0 100644
--- a/src/app/core/debug/debug-model.spec.ts
+++ b/src/app/core/debug/debug-model.spec.ts
@@ -91,7 +91,7 @@
dateFields.forEach(f => {
const date = isolatedScope.parseField(f, model[f]);
- expect(date).toEqual('Thu Aug 17 2017 15:45:20 GMT-0700 (PDT)');
+ expect(date).toEqual(new Date(model[f] * 1000).toString());
});
});
diff --git a/src/app/core/header/header.html b/src/app/core/header/header.html
index 5249a3f..56cf7f1 100644
--- a/src/app/core/header/header.html
+++ b/src/app/core/header/header.html
@@ -52,20 +52,7 @@
typeahead-template-url="customTemplate.html"
typeahead-on-select="vm.routeSelected($item, $model, $label)">
</form>
- <ul class="nav navbar-nav navbar-right">
- <!--<li class="dropdown">-->
- <!--<a ui-sref="main.versions">-->
- <!--<i class="fa fa-bell"></i>-->
- <!--<span ng-if="vm.newNotifications.length > 0" class="label label-warning pull-right">{{vm.newNotifications.length}}</span>-->
- <!--</a>-->
- <!--</li>-->
- <li class=" profil-link">
- <a ui-sref="commonviews.login">
- <span class="profile-address">{{vm.userEmail}}</span>
- <img src="../../images/profile.jpg" class="img-circle" alt="">
- </a>
- </li>
- </ul>
</div>
</div>
-</nav>
\ No newline at end of file
+</nav>
+
diff --git a/src/app/core/header/header.spec.ts b/src/app/core/header/header.spec.ts
index a4a7d90..3437b99 100644
--- a/src/app/core/header/header.spec.ts
+++ b/src/app/core/header/header.spec.ts
@@ -143,10 +143,6 @@
// }, 'global');
});
- it('should print user email', () => {
- expect($('.profile-address', element).text()).toBe('test@xos.us');
- });
-
it('should configure toastr', () => {
delete MockToastrConfig['onTap'];
diff --git a/src/app/core/nav/nav.scss b/src/app/core/nav/nav.scss
index 4897dec..55725b8 100644
--- a/src/app/core/nav/nav.scss
+++ b/src/app/core/nav/nav.scss
@@ -51,4 +51,4 @@
margin-top: 20px;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/app/core/services/navigation.spec.ts b/src/app/core/services/navigation.spec.ts
index bc27772..b9cb84c 100644
--- a/src/app/core/services/navigation.spec.ts
+++ b/src/app/core/services/navigation.spec.ts
@@ -116,7 +116,7 @@
service.add(testRoute);
service.add(testRoute);
expect($log.warn).toHaveBeenCalled();
- expect($log.warn).toHaveBeenCalledWith(`[XosNavigation] Route with label: ${testRoute.label}, state: ${testRoute.state} and parent: ${testRoute.parent} already exist`);
+ expect($log.warn).toHaveBeenCalledWith(`[XosNavigation] Route with label: ${testRoute.label}, state: ${testRoute.state} and parent: ${testRoute.parent} already exists`);
expect(service.query()).toEqual(defaultRoutes.concat([testRoute]));
});
});
diff --git a/src/app/core/services/navigation.ts b/src/app/core/services/navigation.ts
index e7fa4ed..660c144 100644
--- a/src/app/core/services/navigation.ts
+++ b/src/app/core/services/navigation.ts
@@ -71,24 +71,27 @@
return this.routes;
}
- add(route: IXosNavigationRoute) {
+ add(route: IXosNavigationRoute, override: boolean = false) {
if (angular.isDefined(route.state) && angular.isDefined(route.url)) {
throw new Error('[XosNavigation] You can\'t provide both state and url');
}
// NOTE factor this out in a separate method an eventually use recursion since we can nest more routes
+ let preExisting = null;
const routeExist = _.findIndex(this.routes, (r: IXosNavigationRoute) => {
- if (r.label === route.label && r.state === route.state && r.parent === route.parent) {
+ if (r.label === route.label && (r.state === route.state || override) && r.parent === route.parent) {
+ preExisting = r;
return true;
}
else if (_.findIndex(r.children, route) > -1) {
+ preExisting = r;
return true;
}
return false;
}) > -1;
- if (routeExist) {
- this.$log.warn(`[XosNavigation] Route with label: ${route.label}, state: ${route.state} and parent: ${route.parent} already exist`);
+ if (routeExist && !override) {
+ this.$log.warn(`[XosNavigation] Route with label: ${route.label}, state: ${route.state} and parent: ${route.parent} already exists`);
return;
}
@@ -97,6 +100,9 @@
const parentRoute = _.find(this.routes, {state: route.parent});
if (angular.isDefined(parentRoute)) {
if (angular.isArray(parentRoute.children)) {
+ if (override) {
+ _.remove(parentRoute.children, r => r === preExisting);
+ }
parentRoute.children.push(route);
}
else {
@@ -104,11 +110,14 @@
}
}
else {
- this.$log.warn(`[XosNavigation] Parent State (${route.parent}) for state: ${route.state} does not exists`);
+ this.$log.warn(`[XosNavigation] Parent State (${route.parent}) for state: ${route.state} does not exist`);
return;
}
}
else {
+ if (override) {
+ _.remove(this.routes, r => r === preExisting);
+ }
this.routes.push(route);
}
}
diff --git a/src/app/datasources/helpers/store.helpers.spec.ts b/src/app/datasources/helpers/store.helpers.spec.ts
index f3b2321..2f617bc 100644
--- a/src/app/datasources/helpers/store.helpers.spec.ts
+++ b/src/app/datasources/helpers/store.helpers.spec.ts
@@ -26,6 +26,7 @@
import {ConfigHelpers} from '../../core/services/helpers/config.helpers';
import {AuthService} from '../rest/auth.rest';
import {IXosModeldefsCache} from './modeldefs.service';
+import {XosFormHelpers} from '../../core/form/form-helpers';
let service: IStoreHelpersService;
let subject: BehaviorSubject<any>;
@@ -42,6 +43,7 @@
.service('ModelRest', ModelRest) // NOTE evaluate mock
.service('StoreHelpers', StoreHelpers)
.service('AuthService', AuthService)
+ .service('XosFormHelpers', XosFormHelpers)
.value('XosModeldefsCache', {
get: jasmine.createSpy('XosModeldefsCache.get'),
getApiUrlFromModel: jasmine.createSpy('XosModeldefsCache.getApiUrlFromModel')
diff --git a/src/app/datasources/rest/model.rest.spec.ts b/src/app/datasources/rest/model.rest.spec.ts
index d545ac2..da4f2f4 100644
--- a/src/app/datasources/rest/model.rest.spec.ts
+++ b/src/app/datasources/rest/model.rest.spec.ts
@@ -22,6 +22,7 @@
import 'angular-cookies';
import {IXosResourceService} from './model.rest';
import {xosDataSources} from '../index';
+import {IXosFormHelpersService} from '../../core/form/form-helpers';
let service: IXosResourceService;
let resource: ng.resource.IResourceClass<any>;
@@ -34,6 +35,10 @@
websocketClient: 'http://xos-test:3000'
};
+const MockFormHelpers: IXosFormHelpersService = {
+ _getFieldFormat: () => 'date'
+};
+
describe('The ModelRest service', () => {
beforeEach(angular.mock.module(xosDataSources));
@@ -41,7 +46,8 @@
beforeEach(() => {
angular.module(xosDataSources)
- .constant('AppConfig', MockAppCfg);
+ .constant('AppConfig', MockAppCfg)
+ .value('XosFormHelpers', MockFormHelpers);
angular.mock.module(xosDataSources);
});
@@ -99,4 +105,31 @@
$scope.$apply();
httpBackend.flush();
});
+
+ describe('when saving a model', () => {
+
+ let item, date;
+ const timestamp = 1509552402000;
+
+ beforeEach(() => {
+ httpBackend.expectPOST(`${MockAppCfg.apiEndpoint}/core/test`)
+ .respond((method, url, req) => {
+ return [200, req];
+ });
+ resource = service.getResource('/core/test');
+ date = new Date(timestamp);
+ item = new resource({date: date.toString()});
+ });
+
+ xit('should convert dates to timestamps', (done) => {
+ item.$save()
+ .then(res => {
+ expect(res.date).toEqual(timestamp);
+ done();
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ done();
+ });
+ });
});
diff --git a/src/app/datasources/rest/model.rest.ts b/src/app/datasources/rest/model.rest.ts
index 6c72d99..f390616 100644
--- a/src/app/datasources/rest/model.rest.ts
+++ b/src/app/datasources/rest/model.rest.ts
@@ -15,37 +15,48 @@
* limitations under the License.
*/
-
+import * as _ from 'lodash';
import {IXosAppConfig} from '../../../index';
+import {IXosFormHelpersService} from '../../core/form/form-helpers';
+
export interface IXosResourceService {
getResource(url: string): ng.resource.IResourceClass<any>;
}
export class ModelRest implements IXosResourceService {
- static $inject = ['$resource', 'AppConfig'];
+ static $inject = ['$resource', 'AppConfig', 'XosFormHelpers'];
/** @ngInject */
constructor(
private $resource: ng.resource.IResourceService,
- private AppConfig: IXosAppConfig
+ private AppConfig: IXosAppConfig,
+ private XosFormHelpers: IXosFormHelpersService
) {
}
public getResource(url: string): ng.resource.IResourceClass<ng.resource.IResource<any>> {
+ const self = this;
const resource: angular.resource.IResourceClass<any> = this.$resource(`${this.AppConfig.apiEndpoint}${url}/:id/`, {id: '@id'}, {
update: { method: 'PUT' },
query: {
method: 'GET',
isArray: true,
transformResponse: (res) => {
- // FIXME chameleon return everything inside "items"
return res.items ? res.items : res;
}
}
});
resource.prototype.$save = function() {
+
+ // NOTE converting dates back to timestamp
+ _.forEach(Object.keys(this), (k: string) => {
+ if (self.XosFormHelpers._getFieldFormat(this[k]) === 'date') {
+ this[k] = new Date(this[k]).getTime();
+ }
+ });
+
if (this.id) {
return this.$update();
} else {
diff --git a/src/app/datasources/stores/model.store.spec.ts b/src/app/datasources/stores/model.store.spec.ts
index 09dc3f9..ce3e2cd 100644
--- a/src/app/datasources/stores/model.store.spec.ts
+++ b/src/app/datasources/stores/model.store.spec.ts
@@ -28,6 +28,7 @@
import {AuthService} from '../rest/auth.rest';
import {XosDebouncer} from '../../core/services/helpers/debounce.helper';
import {IXosModeldefsCache} from '../helpers/modeldefs.service';
+import {XosFormHelpers} from '../../core/form/form-helpers';
let service: IXosModelStoreService;
let httpBackend: ng.IHttpBackendService;
@@ -70,6 +71,7 @@
.service('XosModelStore', XosModelStore)
.service('ConfigHelpers', ConfigHelpers) // TODO mock
.service('AuthService', AuthService)
+ .service('XosFormHelpers', XosFormHelpers)
.constant('AppConfig', MockAppCfg)
.value('XosModeldefsCache', {
get: jasmine.createSpy('XosModeldefsCache.get').and.returnValue({}),