[CORD-2324] Position the nodes not defined in the constraints
Change-Id: I712a90828e0b6a12b31f62f7391feee64c123f2c
(cherry picked from commit 35fdf249b31b003a163ee0582a77c91f15782587)
diff --git a/src/app/core/confirm/confirm.html b/src/app/core/confirm/confirm.html
index c9b4c81..aacaa8b 100644
--- a/src/app/core/confirm/confirm.html
+++ b/src/app/core/confirm/confirm.html
@@ -19,9 +19,9 @@
<h4>{{vm.config.header}}</h4>
</div>
<div class="modal-body" ng-show="vm.config.text">
- {{vm.config.text}}
+ <div ng-bind-html="vm.config.text"></div>
</div>
<div class="modal-footer">
- <a class="btn btn-default" ng-click="vm.dismiss()">Cancel</a>
+ <a class="btn btn-default" ng-click="vm.dismiss()">Close</a>
<a class="btn" ng-class="action.class" ng-repeat="action in vm.config.actions" ng-click="vm.close(action.cb())">{{action.label}}</a>
</div>
\ No newline at end of file
diff --git a/src/app/core/index.ts b/src/app/core/index.ts
index bdd0157..3f9d503 100644
--- a/src/app/core/index.ts
+++ b/src/app/core/index.ts
@@ -31,6 +31,7 @@
import {xosForm} from './form/form';
import {xosField} from './field/field';
import 'angular-toastr';
+import 'angular-sanitize';
import {xosAlert} from './alert/alert';
import {xosValidation} from './validation/validation';
import {xosSidePanel} from './side-panel/side-panel';
@@ -53,10 +54,11 @@
angular
.module('xosCore', [
+ 'ngSanitize',
'ui.router',
'toastr',
'ui.bootstrap.typeahead',
- 'ui.bootstrap.tabs'
+ 'ui.bootstrap.tabs',
])
.config(routesConfig)
.provider('XosRuntimeStates', XosRuntimeStates)
diff --git a/src/app/service-graph/components/graph/graph.component.ts b/src/app/service-graph/components/graph/graph.component.ts
index 5769b6f..5f84384 100644
--- a/src/app/service-graph/components/graph/graph.component.ts
+++ b/src/app/service-graph/components/graph/graph.component.ts
@@ -87,6 +87,10 @@
this.$log.info(`[XosServiceGraph] Received event: xos.sg.update`);
this.renderGraph(this.graph);
});
+
+ $(window).resize(() => {
+ this.renderGraph(this.graph);
+ });
}
$onDestroy() {
diff --git a/src/app/service-graph/interfaces.ts b/src/app/service-graph/interfaces.ts
index 861ba75..4f2e470 100644
--- a/src/app/service-graph/interfaces.ts
+++ b/src/app/service-graph/interfaces.ts
@@ -22,7 +22,7 @@
export interface IXosSgNode extends Id3Element {
id: string;
- data: any; // this can be a Service, ServiceInstance or Instance
+ data: IXosBaseModel; // this can be a Service, ServiceInstance or Instance
// do we need those?
type: string;
diff --git a/src/app/service-graph/services/node-positioner.service.spec.ts b/src/app/service-graph/services/node-positioner.service.spec.ts
index 14fb9c4..c6308e9 100644
--- a/src/app/service-graph/services/node-positioner.service.spec.ts
+++ b/src/app/service-graph/services/node-positioner.service.spec.ts
@@ -43,7 +43,8 @@
beforeEach(() => {
angular.module('XosNodePositioner', [])
.service('XosNodePositioner', XosNodePositioner)
- .value('ModelRest', mockModelRest);
+ .value('ModelRest', mockModelRest)
+ .value('XosConfirm', {});
angular.mock.module('XosNodePositioner');
});
@@ -125,4 +126,30 @@
scope.$apply();
});
+
+ it('should set unpositioned nodes at the top', (done) => {
+ const svg = {width: 300, height: 200};
+ const nodes = [
+ {data: {name: 'a'}},
+ {data: {name: 'b'}},
+ {data: {name: 'c'}, type: 'service'},
+ {data: {name: 'd'}, type: 'service'}
+ ];
+ constraints = '["a", "b"]';
+
+ service.positionNodes(svg, nodes)
+ .then((positioned) => {
+ expect(positioned[0].x).toBe(100);
+ expect(positioned[0].y).toBe(100);
+ expect(positioned[1].x).toBe(200);
+ expect(positioned[1].y).toBe(100);
+ expect(positioned[2].x).toBe(100);
+ expect(positioned[2].y).toBe(150);
+ expect(positioned[3].x).toBe(200);
+ expect(positioned[3].y).toBe(150);
+ done();
+ });
+
+ scope.$apply();
+ });
});
diff --git a/src/app/service-graph/services/node-positioner.service.ts b/src/app/service-graph/services/node-positioner.service.ts
index 499afd0..17823b7 100644
--- a/src/app/service-graph/services/node-positioner.service.ts
+++ b/src/app/service-graph/services/node-positioner.service.ts
@@ -18,6 +18,7 @@
import * as _ from 'lodash';
import {IXosResourceService} from '../../datasources/rest/model.rest';
import {IXosSgNode} from '../interfaces';
+import {IXosConfirm} from '../../core/confirm/confirm.service';
export interface IXosNodePositioner {
positionNodes(svg: {width: number, height: number}, nodes: any[]): ng.IPromise<IXosSgNode[]>;
@@ -27,51 +28,70 @@
static $inject = [
'$log',
'$q',
- 'ModelRest'
+ 'ModelRest',
+ 'XosConfirm'
];
constructor (
private $log: ng.ILogService,
private $q: ng.IQService,
private ModelRest: IXosResourceService,
+ private XosConfirm: IXosConfirm
) {
this.$log.info('[XosNodePositioner] Setup');
}
- public positionNodes(svg: {width: number, height: number}, nodes: any[]): ng.IPromise<IXosSgNode[]> {
+ public positionNodes(svg: {width: number, height: number}, nodes: IXosSgNode[]): ng.IPromise<IXosSgNode[]> {
- // TODO refactor naming in this loop to make it clearer
const d = this.$q.defer();
this.getConstraints()
.then(constraints => {
const hStep = this.getHorizontalStep(svg.width, constraints);
- const positionConstraints = _.reduce(constraints, (all: any, c: string | string[], i: number) => {
- let pos: {x: number, y: number, fixed: boolean} = {
- x: svg.width / 2,
- y: svg.height / 2,
- fixed: true
- };
+ const positionConstraints = _.reduce(constraints, (all: any, horizontalConstraint: string | string[], i: number) => {
// NOTE it's a single element, leave it in the middle
- if (angular.isString(c)) {
- pos.x = (i + 1) * hStep;
- all[c] = pos;
+ if (angular.isString(horizontalConstraint)) {
+ all[horizontalConstraint] = {
+ x: (i + 1) * hStep,
+ y: svg.height / 2,
+ fixed: true
+ };
}
else {
- const verticalConstraints = c;
+ const verticalConstraints = horizontalConstraint;
const vStep = this.getVerticalStep(svg.height, verticalConstraints);
- _.forEach(verticalConstraints, (c: string, v: number) => {
- if (angular.isString(c)) {
- let p = angular.copy(pos);
- p.x = (i + 1) * hStep;
- p.y = (v + 1) * vStep;
- all[c] = p;
+ _.forEach(verticalConstraints, (verticalConstraint: string, v: number) => {
+ if (angular.isString(verticalConstraint)) {
+ all[verticalConstraint] = {
+ x: (i + 1) * hStep,
+ y: (v + 1) * vStep,
+ fixed: true
+ };
}
});
}
return all;
}, {});
+ // find the nodes that don't have a position defined and put them at the top
+ const allNodes = _.reduce(nodes, (all: string[], n: IXosSgNode) => {
+ if (n.type === 'service') {
+ all.push(n.data.name);
+ }
+ return all;
+ }, []);
+ const positionedNodes = Object.keys(positionConstraints);
+ const unpositionedNodes = _.difference(allNodes, positionedNodes);
+
+ _.forEach(unpositionedNodes, (node: string, i: number) => {
+ const hStep = this.getHorizontalStep(svg.width, unpositionedNodes);
+ positionConstraints[node] = {
+ x: (i + 1) * hStep,
+ y: svg.height - 50,
+ fixed: true
+ };
+ });
+
d.resolve(_.map(nodes, n => {
return angular.merge(n, positionConstraints[n.data.name]);
}));
@@ -90,6 +110,17 @@
d.resolve(JSON.parse(res[0].constraints));
})
.catch(e => {
+ this.XosConfirm.open({
+ header: 'Error in graph constraints config',
+ text: `
+ There was an error in the settings you provided as graph constraints.
+ Please check the declaration of the <code>"Graph Constraints"</code> model. <br/>
+ The error was: <br/><br/>
+ <code>${e}</code>
+ <br/><br/>
+ Please fix it to see the graph.`,
+ actions: []
+ });
d.reject(e);
});
return d.promise;
diff --git a/src/app/service-graph/services/renderer/node.renderer.ts b/src/app/service-graph/services/renderer/node.renderer.ts
index 17bcf70..fb29046 100644
--- a/src/app/service-graph/services/renderer/node.renderer.ts
+++ b/src/app/service-graph/services/renderer/node.renderer.ts
@@ -50,15 +50,13 @@
.selectAll('g.node')
.data(nodes, n => n.id);
- node
- .call(this.drag);
-
const entering = node.enter()
.append('g')
.attr({
id: n => n.id,
class: n => `node ${n.type} ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
- });
+ })
+ .call(this.drag);
this.renderServiceNodes(entering.filter('.service'));
this.renderServiceInstanceNodes(entering.filter('.serviceinstance'));