Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui: Discovery Chain Continued #6939

Merged
merged 14 commits into from
Dec 17, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 104 additions & 50 deletions ui-v2/app/components/discovery-chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@ import { inject as service } from '@ember/service';
import { set, get, computed } from '@ember/object';
import { next } from '@ember/runloop';

const getNodesByType = function(nodes = {}, type) {
return Object.values(nodes).filter(item => item.Type === type);
};

const targetsToFailover = function(targets, a) {
let type;
const Targets = targets.map(function(b) {
// FIXME: this isn't going to work past namespace for services
// with dots in the name
const [aRev, bRev] = [a, b].map(item => item.split('.').reverse());
const types = ['Datacenter', 'Namespace', 'Service', 'Subset'];
return bRev.find(function(item, i) {
const res = item !== aRev[i];
if (res) {
type = types[i];
}
return res;
});
});
return {
Type: type,
Targets: Targets,
};
};
const getNodeResolvers = function(nodes = {}) {
const failovers = getFailovers(nodes);
const resolvers = {};
Expand All @@ -15,7 +39,7 @@ const getNodeResolvers = function(nodes = {}) {
return resolvers;
};

const getTargetResolvers = function(targets = [], nodes = {}) {
const getTargetResolvers = function(dc, nspace = 'default', targets = [], nodes = {}) {
const resolvers = {};
Object.values(targets).forEach(item => {
let node = nodes[item.ID];
Expand All @@ -24,8 +48,9 @@ const getTargetResolvers = function(targets = [], nodes = {}) {
resolvers[item.Service] = {
ID: item.ID,
Name: item.Service,
Subsets: [],
Children: [],
Failover: null,
Redirect: null,
};
}
const resolver = resolvers[item.Service];
Expand All @@ -37,12 +62,18 @@ const getTargetResolvers = function(targets = [], nodes = {}) {
const temp = item.ID.split('.');
temp.shift();
resolver.ID = temp.join('.');
resolver.Subsets.push(item);
resolver.Children.push(item);
}
if (typeof node.Resolver.Failover !== 'undefined') {
// FIXME: Figure out if we can get rid of this
/* eslint ember/no-side-effects: "warn" */
set(failoverable, 'Failover', targetsToFailover(node.Resolver.Failover.Targets, item.ID));
} else {
const res = targetsToFailover([node.Resolver.Target], `service.${nspace}.${dc}`);
if (res.Type === 'Datacenter' || res.Type === 'Namespace') {
resolver.Children.push(item);
set(failoverable, 'Redirect', true);
}
}
}
});
Expand All @@ -59,68 +90,102 @@ const getFailovers = function(nodes = {}) {
});
return failovers;
};
const getType = function(nodes = {}, type) {
return Object.values(nodes).filter(item => item.Type === type);
};

const targetsToFailover = function(targets, a) {
let type;
const Targets = targets.map(function(b) {
// FIXME: this isn't going to work past namespace for services
// with dots in the name
const [aRev, bRev] = [a, b].map(item => item.split('.').reverse());
const types = ['Datacenter', 'Namespace', 'Service', 'Subset'];
return bRev.find(function(item, i) {
const res = item !== aRev[i];
if (res) {
type = types[i];
}
return res;
});
});
return {
Type: type,
Targets: Targets,
};
};

export default Component.extend({
dom: service('dom'),
ticker: service('ticker'),
dataStructs: service('data-structs'),
classNames: ['discovery-chain'],
classNameBindings: ['active'],
isDisplayed: false,
selectedId: '',
x: 0,
y: 0,
tooltip: '',
activeTooltip: false,
init: function() {
this._super(...arguments);
this._listeners = this.dom.listeners();
this._viewportlistener = this.dom.listeners();
},
didInsertElement: function() {
this._super(...arguments);
this._viewportlistener.add(
this.dom.isInViewport(this.element, bool => {
set(this, 'isDisplayed', bool);
if (this.isDisplayed) {
this.addPathListeners();
} else {
this.ticker.destroy(this);
}
})
);
},
didReceiveAttrs: function() {
this._super(...arguments);
if (this.element) {
this.addPathListeners();
}
},
willDestroyElement: function() {
this._super(...arguments);
this._listeners.remove();
this._viewportlistener.remove();
this.ticker.destroy(this);
},
splitters: computed('chain.Nodes', function() {
return getType(get(this, 'chain.Nodes'), 'splitter').map(function(item) {
return getNodesByType(get(this, 'chain.Nodes'), 'splitter').map(function(item) {
set(item, 'ID', `splitter:${item.Name}`);
return item;
});
}),
routers: computed('chain.Nodes', function() {
// Right now there should only ever be one 'Router'.
return getType(get(this, 'chain.Nodes'), 'router');
return getNodesByType(get(this, 'chain.Nodes'), 'router');
}),
routes: computed('routers', function() {
return get(this, 'routers').reduce(function(prev, item) {
routes: computed('chain', 'routers', function() {
const routes = get(this, 'routers').reduce(function(prev, item) {
return prev.concat(
item.Routes.map(function(route, i) {
return {
...route,
ID: `route:${item.Name}-${i}`,
ID: `route:${item.Name}-${JSON.stringify(route.Definition.Match.HTTP)}`,
};
})
);
}, []);
if (routes.length === 0) {
let nextNode = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Datacenter}`;
const splitterID = `splitter:${this.chain.ServiceName}`;
if (typeof this.chain.Nodes[splitterID] !== 'undefined') {
nextNode = splitterID;
}
routes.push({
Default: true,
ID: `route:${this.chain.ServiceName}`,
Name: this.chain.ServiceName,
Definition: {
Match: {
HTTP: {
PathPrefix: '/',
},
},
},
NextNode: nextNode,
});
}
return routes;
}),
nodeResolvers: computed('chain.Nodes', function() {
return getNodeResolvers(get(this, 'chain.Nodes'));
}),
resolvers: computed('nodeResolvers.[]', 'chain.Targets', function() {
return getTargetResolvers(get(this, 'chain.Targets'), this.nodeResolvers);
return getTargetResolvers(
this.chain.Datacenter,
this.chain.Namespace,
get(this, 'chain.Targets'),
this.nodeResolvers
);
}),
graph: computed('chain.Nodes', function() {
const graph = this.dataStructs.graph();
Expand All @@ -133,7 +198,10 @@ export default Component.extend({
break;
case 'router':
item.Routes.forEach(function(route, i) {
graph.addLink(`route:${item.Name}-${i}`, route.NextNode);
graph.addLink(
`route:${item.Name}-${JSON.stringify(route.Definition.Match.HTTP)}`,
route.NextNode
);
});
break;
}
Expand Down Expand Up @@ -168,27 +236,16 @@ export default Component.extend({
edges: edges.map(item => `#${CSS.escape(item)}`),
};
}),
width: computed('chain.{Nodes,Targets}', function() {
width: computed('isDisplayed', 'chain.{Nodes,Targets}', function() {
return this.element.offsetWidth;
}),
height: computed('chain.{Nodes,Targets}', function() {
height: computed('isDisplayed', 'chain.{Nodes,Targets}', function() {
return this.element.offsetHeight;
}),
didInsertElement: function() {
this.addPathListeners();
},
didReceiveAttrs: function() {
if (this.element) {
this.addPathListeners();
}
},
// TODO(octane): ember has trouble adding mouse events to svg elements whilst giving
// the developer access to the mouse event therefore we just use JS to add our events
// revisit this post Octane
addPathListeners: function() {
if (typeof this._listeners === 'undefined') {
this._listeners = this.dom.listeners();
}
// FIXME: Figure out if we can remove this next
next(() => {
this._listeners.remove();
Expand All @@ -206,9 +263,6 @@ export default Component.extend({
// the tooltip would stay if there was no change to the <path>
// set(this, 'activeTooltip', false);
},
willDestroyElement: function() {
this._listeners.remove();
},
actions: {
showSplit: function(e) {
this.setProperties({
Expand Down
6 changes: 6 additions & 0 deletions ui-v2/app/components/route-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { get, computed } from '@ember/object';
export default Component.extend({
tagName: '',
path: computed('item', function() {
if (get(this, 'item.Default')) {
return {
type: 'Default',
value: '/',
};
}
return Object.entries(get(this, 'item.Definition.Match.HTTP') || {}).reduce(
function(prev, [key, value]) {
if (key.toLowerCase().startsWith('path')) {
Expand Down
6 changes: 1 addition & 5 deletions ui-v2/app/controllers/dc/services/show.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ export default Controller.extend(WithEventSource, WithSearching, {
// as this is a variable used purely for view level things, if the view was different we might not
// need this variable

// set(this, 'selectedTab', 'instances');
// FIXME: Just to make it easier to build for the moment
// We'll also need to use a similar or the same approach as our
// didAppear thing see components/code-editor.js plus others
set(this, 'selectedTab', 'routing');
set(this, 'selectedTab', 'instances');
},
item: listen('item').catch(function(e) {
if (e.target.readyState === 1) {
Expand Down
2 changes: 2 additions & 0 deletions ui-v2/app/helpers/css-var.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions ui-v2/app/helpers/dom-position.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { inject as service } from '@ember/service';

export default Helper.extend({
dom: service('dom'),
compute: function([selector], hash) {
compute: function([selector, id], hash) {
const $el = this.dom.element(selector);
let $refs = [$el.offsetParent, $el];
// TODO: helper probably need to accept a `reference=` option
const $refs = [$el.offsetParent, $el];
// TODO: helper probably needs to accept a `reference=` option
// with a selector to use as reference/root
if (selector.startsWith('#resolver:')) {
$refs.unshift($refs[0].offsetParent);
}
const pos = $refs.reduce(
return $refs.reduce(
function(prev, item) {
prev.x += item.offsetLeft;
prev.y += item.offsetTop;
Expand All @@ -24,6 +24,5 @@ export default Helper.extend({
width: $el.offsetWidth,
}
);
return pos;
},
});
8 changes: 5 additions & 3 deletions ui-v2/app/helpers/svg-curve.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { helper } from '@ember/component/helper';
// arguments should be a list of {x: numLike, y: numLike} points
// numLike meaning they should be numbers (or numberlike strings i.e. "1" vs 1)
const curve = function() {
const args = [...arguments];
// our arguments are destination first control points last
// SVGs are control points first destination last
// we 'shift,push' to turn that around and then map
// through the values to convert it to 'x y, x y' etc
// whether the curve is bezier (C) or quadratic (Q)
// whether the curve is cubic-bezier (C) or quadratic-bezier (Q)
// then depends on the amount of control points
// `Q|C x y, x y, x y` etc
return `${arguments.length > 2 ? `C` : `Q`} ${args
Expand All @@ -21,15 +23,15 @@ const move = function(d) {

export default helper(function([dest], hash) {
const src = hash.src || { x: 0, y: 0 };
const equation = hash.equation || 'bezier';
const type = hash.type || 'cubic';
let args = [
dest,
{
x: (src.x + dest.x) / 2,
y: src.y,
},
];
if (equation === 'bezier') {
if (type === 'cubic') {
args.push({
x: args[1].x,
y: dest.y,
Expand Down
9 changes: 9 additions & 0 deletions ui-v2/app/helpers/tween-to.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';

export default Helper.extend({
ticker: service('ticker'),
compute: function([props, id], hash) {
return this.ticker.tweenTo(props, id);
},
});
2 changes: 1 addition & 1 deletion ui-v2/app/services/data-structs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Service from '@ember/service';
import createGraph from 'ngraph.graph';

export default Service.extend({
graph: function(nodes) {
graph: function() {
return createGraph();
},
});
Loading