[CORD-1001] Solved race condition in autogeneration of models and tweaks for slow connections
Change-Id: Ifcbc4f9057f04e19703af96f8d8294f2b975be66
diff --git a/conf/proxy.js b/conf/proxy.js
index daec1a8..6a7e8b0 100644
--- a/conf/proxy.js
+++ b/conf/proxy.js
@@ -1,7 +1,7 @@
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({
- target: 'http://xos.dev:9101'
+ target: 'http://192.168.46.100:9101'
});
proxy.on('error', function(error, req, res) {
diff --git a/conf/webpack-dist.conf.js b/conf/webpack-dist.conf.js
index d0300a6..924f2de 100644
--- a/conf/webpack-dist.conf.js
+++ b/conf/webpack-dist.conf.js
@@ -91,7 +91,8 @@
},
entry: {
app: `./${conf.path.src('index')}`,
- vendor: Object.keys(pkg.dependencies)
+ vendor: Object.keys(pkg.dependencies),
+ loader: `./${conf.path.src('/app/style/imports/loader.scss')}`
},
ts: {
configFileName: 'tsconfig.json'
diff --git a/conf/webpack.conf.js b/conf/webpack.conf.js
index 8bef911..5506296 100644
--- a/conf/webpack.conf.js
+++ b/conf/webpack.conf.js
@@ -5,6 +5,7 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');
const CopyWebpackPlugin = require('copy-webpack-plugin');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
const env = process.env.NODE_ENV || 'production';
const brand = process.env.BRAND || 'cord';
@@ -27,13 +28,15 @@
},
{
test: /\.(css|scss)$/,
- loaders: [
- 'style',
- 'css',
- 'resolve-url-loader',
- 'sass?sourceMap',
- 'postcss'
- ]
+ loaders: ExtractTextPlugin.extract({
+ fallbackLoader: 'style',
+ loader: [
+ 'css',
+ 'resolve-url-loader',
+ 'sass?sourceMap',
+ 'postcss'
+ ]
+ })
},
{
test: /\.ts$/,
@@ -66,14 +69,15 @@
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
template: conf.path.src('index.html')
- })
+ }),
+ new ExtractTextPlugin('index-[contenthash].css'),
],
postcss: () => [autoprefixer],
debug: true,
devtool: 'source-map',
output: {
path: path.join(process.cwd(), conf.paths.tmp),
- filename: 'index.js'
+ filename: '[name].js'
},
resolve: {
extensions: [
@@ -84,7 +88,10 @@
'.ts'
]
},
- entry: `./${conf.path.src('index')}`,
+ entry: {
+ indes: `./${conf.path.src('index')}`,
+ loader: `./${conf.path.src('/app/style/imports/loader.scss')}`
+ },
ts: {
configFileName: 'tsconfig.json'
},
diff --git a/package.json b/package.json
index e97f9a0..fa8fb46 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"bootstrap-sass": "^3.3.7",
"jquery": "^3.1.1",
"lodash": "^4.17.2",
+ "ngprogress": "^1.1.1",
"oclazyload": "^1.0.9",
"pluralize": "^3.1.0",
"rxjs": "^5.0.1",
diff --git a/src/app/datasources/helpers/model-discoverer.service.ts b/src/app/datasources/helpers/model-discoverer.service.ts
index da6f265..434f74e 100644
--- a/src/app/datasources/helpers/model-discoverer.service.ts
+++ b/src/app/datasources/helpers/model-discoverer.service.ts
@@ -34,10 +34,12 @@
'ConfigHelpers',
'XosRuntimeStates',
'XosNavigationService',
- 'XosModelStore'
+ 'XosModelStore',
+ 'ngProgressFactory'
];
private xosModels: IXosModel[] = []; // list of augmented model definitions;
private xosServices: string[] = []; // list of loaded services
+ private progressBar;
constructor (
private $log: ng.ILogService,
@@ -46,8 +48,11 @@
private ConfigHelpers: IXosConfigHelpersService,
private XosRuntimeStates: IXosRuntimeStatesService,
private XosNavigationService: IXosNavigationService,
- private XosModelStore: IXosModelStoreService
+ private XosModelStore: IXosModelStoreService,
+ private ngProgressFactory: any // check for type defs
) {
+ this.progressBar = this.ngProgressFactory.createInstance();
+ this.progressBar.setColor('#f6a821');
}
public get(modelName: string): IXosModel|null {
@@ -56,7 +61,7 @@
public discover() {
const d = this.$q.defer();
-
+ this.progressBar.start();
this.XosModelDefs.get()
.then((modelsDef: IXosModeldef[]) => {
@@ -81,23 +86,28 @@
})
.then(model => {
this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
- return this.$q.resolve();
+ return this.$q.resolve('true');
})
.catch(err => {
this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`);
- // NOTE why this does not resolve?????
- // return this.$q.resolve();
- return this.$q.reject();
+ return this.$q.resolve('false');
});
pArray.push(p);
});
this.$q.all(pArray)
- .then(() => {
+ .then((res) => {
+ // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
+ // that's because we want to wait anyway for all the models to be loaded
+ if (res.indexOf('false') > -1) {
+ d.resolve(false);
+ }
d.resolve(true);
- this.$log.info('[XosModelDiscovererService] All models loaded!');
})
.catch(() => {
d.resolve(false);
+ })
+ .finally(() => {
+ this.progressBar.complete();
});
});
return d.promise;
@@ -210,19 +220,19 @@
private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
const d = this.$q.defer();
- let called = false;
+ let populated = false;
const apiUrl = this.getApiUrlFromModel(model);
this.XosModelStore.query(model.name, apiUrl)
.subscribe(
() => {
// skipping the first response as the observable gets created as an empty array
- if (called) {
+ if (populated) {
return d.resolve(model);
}
- called = true;
+ populated = true;
},
err => {
- d.reject(err);
+ return d.reject(err);
}
);
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index 4958015..61eb114 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -24,10 +24,15 @@
public query(modelName: string, apiUrl: string): Observable<any> {
// if there isn't already an observable for that item
+ // create a new one and .next() is called by this.loadInitialData once data are received
if (!this._collections[modelName]) {
this._collections[modelName] = new BehaviorSubject([]); // NOTE maybe this can be created when we get response from the resource
this.loadInitialData(modelName, apiUrl);
}
+ // else manually trigger the next with the last know value to trigger the subscribe method of who's requestiong this data
+ else {
+ this._collections[modelName].next(this._collections[modelName].value);
+ }
this.webSocket.list()
.filter((e: IWSEvent) => e.model === modelName)
@@ -76,7 +81,7 @@
}
private loadInitialData(model: string, apiUrl?: string) {
- // TODO provide alway the apiUrl togheter with the query() params
+ // TODO provide always the apiUrl togheter with the query() params
if (!angular.isDefined(apiUrl)) {
// NOTE check what is the correct pattern to pluralize this
apiUrl = this.storeHelpers.urlFromCoreModel(model);
@@ -87,10 +92,8 @@
this._collections[model].next(res);
})
.catch(
- // TODO understand how to send an error to an observable
err => {
this._collections[model].error(err);
- // this.$log.log(`Error retrieving ${model}`, err);
}
);
}
diff --git a/src/app/style/imports/loader.scss b/src/app/style/imports/loader.scss
new file mode 100644
index 0000000..4c1a751
--- /dev/null
+++ b/src/app/style/imports/loader.scss
@@ -0,0 +1,45 @@
+@import '../vars';
+
+.loader,
+.loader:before,
+.loader:after {
+ background: $color-accent;
+ animation: loaderAnimation 1s infinite ease-in-out;
+ width: 1em;
+ height: 4em;
+}
+.loader {
+ color: $color-accent;
+ text-indent: -9999em;
+ margin: 88px auto;
+ position: relative;
+ font-size: 11px;
+ transform: translateZ(0);
+ animation-delay: -0.16s;
+}
+.loader:before,
+.loader:after {
+ position: absolute;
+ top: 0;
+ content: '';
+}
+.loader:before {
+ left: -1.5em;
+ animation-delay: -0.32s;
+}
+.loader:after {
+ left: 1.5em;
+}
+
+@keyframes loaderAnimation {
+ 0%,
+ 80%,
+ 100% {
+ box-shadow: 0 0;
+ height: 4em;
+ }
+ 40% {
+ box-shadow: 0 -2em;
+ height: 5em;
+ }
+}
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index 2f5ee65..9ffc4c8 100644
--- a/src/index.html
+++ b/src/index.html
@@ -14,6 +14,11 @@
<body class="{{class}}">
<ui-view></ui-view>
+ <div ng-show="::false">
+ <div class="loader">
+ Loading
+ </div>
+ </div>
</body>
<script type="text/javascript" src="./app.config.js"></script>
<script type="text/javascript" src="./style.config.js"></script>
diff --git a/src/index.scss b/src/index.scss
index 2d28146..08318a1 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -3,6 +3,7 @@
@import './app/style/style.scss';
@import './app/style/stroke-icons/style.css';
@import './app/style/pe-icons/pe-icon-7-stroke.css';
+@import '../node_modules/ngprogress/ngProgress.css';
html, body.blank {
height: 100%;
@@ -16,6 +17,14 @@
// BOOTSTRAP OVERRIDES
// TODO Clean app/style/style.scss
+// ngProgress
+#ngProgress-container {
+ position: absolute;
+ width: 100%;
+ top: 0;
+ z-index: 10000;
+}
+
// Alternate styles
//
// Generate contextual modifier classes for colorizing the alert.
diff --git a/src/index.ts b/src/index.ts
index de9d9bf..6c34b57 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,6 +6,7 @@
import 'angular-ui-router';
import 'angular-resource';
import 'angular-cookies';
+import '../node_modules/ngprogress/build/ngProgress';
import routesConfig from './routes';
import {main} from './app/main';
@@ -48,9 +49,10 @@
xosDataSources,
xosViews,
xosExtender,
+ xosTemplate, // template module
'ui.router',
'ngResource',
- xosTemplate // template module
+ 'ngProgress'
])
.config(XosLogDecorator)
.config(routesConfig)
@@ -121,6 +123,8 @@
// after setting up dynamic routes, redirect to previous state
$location.path(lastRoute).search(lastQueryString);
+ })
+ .finally(() => {
$rootScope.$emit('xos.core.modelSetup');
});
}
diff --git a/src/interceptors.ts b/src/interceptors.ts
index 0a2bf74..a79765e 100644
--- a/src/interceptors.ts
+++ b/src/interceptors.ts
@@ -8,12 +8,16 @@
export function userStatusInterceptor($state: angular.ui.IStateService, $cookies: ng.cookies.ICookiesService, $q: ng.IQService) {
const checkLogin = (res) => {
- if (res.status === 401 || res.status === -1) {
- $cookies.remove('sessionid', {path: '/'});
- $state.go('login');
- return $q.reject(res);
+ switch (res.status) {
+ case -1:
+ case 401:
+ case 500:
+ $cookies.remove('sessionid', {path: '/'});
+ $state.go('login');
+ return $q.reject(res);
+ default:
+ return res;
}
- return res;
};
return {