ui: Move discovery chain component (#9154)

This commit is contained in:
John Cowen 2020-11-11 14:43:37 +00:00 committed by hashicorp-ci
parent 85595ab3ea
commit c52f03156c
21 changed files with 209 additions and 210 deletions

View File

@ -0,0 +1,148 @@
<style>
{{#if selected.nodes }}
{{selected.nodes}} {
opacity: 1 !important;
background-color: var(--white);
border: var(--decor-border-100);
border-radius: var(--decor-radius-200);
border-color: var(--gray-500);
box-shadow: var(--decor-elevation-600);
}
{{/if}}
{{#if selected.edges }}
{{selected.edges}} {
opacity: 1;
}
{{/if}}
</style>
<div class="routes">
<header>
<h2>
{{chain.ServiceName}} Router
<span>
<em role="tooltip">Use routers to intercept traffic using L7 criteria such as path prefixes or http headers.</em>
</span>
</h2>
</header>
<div role="group">
{{#each routes as |item|}}
<Consul::DiscoveryChain::RouteCard
@item={{item}}
@onclick={{action "click"}}
/>
{{/each}}
</div>
</div>
<div class="splitters">
<header>
<h2>
Splitters
<span>
<em role="tooltip">Splitters are configured to split incoming requests across different services or subsets of a single service.</em>
</span>
</h2>
</header>
<div role="group">
{{#each (sort-by 'Name' splitters) as |item|}}
<Consul::DiscoveryChain::SplitterCard
@item={{item}}
@onclick={{action "click"}}
/>
{{/each}}
</div>
</div>
<div class="resolvers">
<header>
<h2>
Resolvers
<span>
<em role="tooltip">Resolvers are used to define which instances of a service should satisfy discovery requests.</em>
</span>
</h2>
</header>
<div role="group">
{{#each (sort-by 'Name' resolvers) as |item|}}
<Consul::DiscoveryChain::ResolverCard
@item={{item}}
@onclick={{action "click"}}
/>
{{/each}}
</div>
</div>
<svg width="100%" height="100%" viewBox={{concat '0 0 ' width ' ' height}} preserveAspectRatio="none">
{{#each routes as |item|}}
{{#let (dom-position (concat '#' item.ID)) as |src|}}
{{#let (dom-position (concat '#' item.NextNode)) as |destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat item.ID)) as |dest|}}
<path
id={{concat item.ID '>' item.NextNode}}
d={{
svg-curve (hash
x=dest.x
y=dest.y
) src=(hash
x=(add src.x src.width)
y=(add src.y (div src.height 2))
)}} />
{{/let}}
{{/let}}
{{/let}}
{{/each}}
{{#each splitters as |splitter|}}
{{#let (dom-position (concat '#' splitter.ID)) as |src|}}
{{#each splitter.Splits as |item index|}}
{{#let (dom-position (concat '#' item.NextNode)) as |destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat splitter.ID '-' index)) as |dest|}}
<path
id={{concat 'splitter:' splitter.Name '>' item.NextNode}}
class="split"
data-percentage={{or item.Weight 0}}
d={{
svg-curve (hash
x=dest.x
y=dest.y
) src=(hash
x=(add src.x src.width)
y=(add src.y (div src.height 2))
)}} />
{{/let}}
{{/let}}
{{/each}}
{{/let}}
{{/each}}
</svg>
<svg class="resolver-inlets" viewBox={{concat '0 0 10 ' height}}>
{{#each routes as |item|}}
{{#if (starts-with 'resolver:' item.NextNode) }}
{{#let (dom-position (concat '#' item.NextNode)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/if}}
{{/each}}
{{#each splitters as |item|}}
{{#each item.Splits as |item|}}
{{#let (dom-position (concat '#' item.NextNode)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/each}}
{{/each}}
</svg>
<svg class="splitter-inlets" viewBox={{concat '0 0 10 ' height}}>
{{#each routes as |item|}}
{{#if (starts-with 'splitter:' item.NextNode) }}
{{#let (dom-position (concat '#' item.NextNode)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/if}}
{{/each}}
</svg>
<div class={{concat 'tooltip' (if activeTooltip ' active' '')}} style={{{ concat 'top:' y 'px;left:' x 'px;'}}}>
<span role="tooltip">{{round tooltip decimals=2}}%</span>
</div>

View File

@ -20,22 +20,6 @@ export default Component.extend({
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 => {
if (get(this, 'isDisplayed') !== bool) {
set(this, 'isDisplayed', bool);
if (this.isDisplayed) {
this.addPathListeners();
} else {
this.ticker.destroy(this);
}
}
})
);
},
didReceiveAttrs: function() {
this._super(...arguments);
@ -46,7 +30,6 @@ export default Component.extend({
willDestroyElement: function() {
this._super(...arguments);
this._listeners.remove();
this._viewportlistener.remove();
this.ticker.destroy(this);
},
splitters: computed('chain.Nodes', function() {
@ -133,10 +116,10 @@ export default Component.extend({
edges: edges.map(item => `#${CSS.escape(item)}`),
};
}),
width: computed('isDisplayed', 'chain.{Nodes,Targets}', function() {
width: computed('chain.{Nodes,Targets}', function() {
return this.element.offsetWidth;
}),
height: computed('isDisplayed', 'chain.{Nodes,Targets}', function() {
height: computed('chain.{Nodes,Targets}', function() {
return this.element.offsetHeight;
}),
// TODO(octane): ember has trouble adding mouse events to svg elements whilst giving

View File

@ -1,4 +1,5 @@
@import './discovery-chain/index';
@import './skin';
@import './layout';
.discovery-chain {
@extend %discovery-chain;
}

View File

@ -1,13 +1,18 @@
<div class="resolver-card">
<header onclick={{onclick}} id={{concat 'resolver:' item.ID}}>
<div
class="resolver-card"
...attributes
>
<header onclick={{optional @onclick}} id={{concat 'resolver:' @item.ID}}>
<a name="">
<h3>{{item.Name}}</h3>
<h3>{{@item.Name}}</h3>
{{#if item.Failover}}
<dl class="failover">
<dt data-tooltip={{concat item.Failover.Type ' failover'}}>{{concat item.Failover.Type ' failover'}}</dt>
<dt data-tooltip={{concat @item.Failover.Type ' failover'}}>
{{concat @item.Failover.Type ' failover'}}
</dt>
<dd>
<ol>
{{#each item.Failover.Targets as |item|}}
{{#each @item.Failover.Targets as |item|}}
<li>
<span>{{item}}</span>
</li>
@ -18,10 +23,10 @@
{{/if}}
</a>
</header>
{{#if (gt item.Children.length 0)}}
{{#if (gt @item.Children.length 0)}}
<ul>
{{#each item.Children as |child|}}
<li onclick={{onclick}} id={{concat 'resolver:' child.ID}}>
{{#each @item.Children as |child|}}
<li onclick={{optional @onclick}} id={{concat 'resolver:' child.ID}}>
<a name="">
{{#if child.Redirect}}
<dl class="redirect">

View File

@ -1,28 +1,32 @@
<a class="route-card" onclick={{onclick}} id={{item.ID}}>
<header class={{if (eq path.value '/') 'short'}}>
{{#if (gt item.Definition.Match.HTTP.Methods.length 0) }}
<a
class="route-card"
onclick={{@onclick}}
id={{@item.ID}}
>
<header class={{if (eq this.path.value '/') 'short'}}>
{{#if (gt @item.Definition.Match.HTTP.Methods.length 0) }}
<ul class="match-methods">
{{#each item.Definition.Match.HTTP.Methods as |item|}}
{{#each @item.Definition.Match.HTTP.Methods as |item|}}
<li>{{item}}</li>
{{/each}}
</ul>
{{/if}}
<dl>
<dt>
{{path.type}}
{{this.path.type}}
</dt>
<dd>
{{path.value}}
{{this.path.value}}
</dd>
</dl>
</header>
{{#if (gt item.Definition.Match.HTTP.Header.length 0) }}
{{#if (gt @item.Definition.Match.HTTP.Header.length 0) }}
<section class="match-headers">
<header data-tooltip="Header">
<h4>Headers</h4>
</header>
<dl>
{{#each item.Definition.Match.HTTP.Header as |item|}}
{{#each @item.Definition.Match.HTTP.Header as |item|}}
<dt>
{{item.Name}}
</dt>
@ -33,13 +37,13 @@
</dl>
</section>
{{/if}}
{{#if (gt item.Definition.Match.HTTP.QueryParam.length 0) }}
{{#if (gt @item.Definition.Match.HTTP.QueryParam.length 0) }}
<section class="match-queryparams">
<header data-tooltip="Query Params">
<h4>Query Params</h4>
</header>
<dl>
{{#each item.Definition.Match.HTTP.QueryParam as |item|}}
{{#each @item.Definition.Match.HTTP.QueryParam as |item|}}
<dt>
{{item.Name}}
</dt>

View File

@ -1,10 +1,9 @@
import Component from '@ember/component';
import { get, computed } from '@ember/object';
import Component from '@glimmer/component';
import { get } from '@ember/object';
export default Component.extend({
tagName: '',
path: computed('item', function() {
return Object.entries(get(this, 'item.Definition.Match.HTTP') || {}).reduce(
export default class RouteCard extends Component {
get path() {
return Object.entries(get(this.args.item, 'Definition.Match.HTTP') || {}).reduce(
function(prev, [key, value]) {
if (key.toLowerCase().startsWith('path')) {
return {
@ -19,5 +18,5 @@ export default Component.extend({
value: '/',
}
);
}),
});
}
}

View File

@ -0,0 +1,13 @@
<div
...attributes
>
<a
id={{@item.ID}}
class="splitter-card"
onclick={{optional @onclick}}
>
<header>
<h3>{{@item.Name}}</h3>
</header>
</a>
</div>

View File

@ -1,141 +0,0 @@
{{#if isDisplayed }}
<style>
{{#if selected.nodes }}
{{selected.nodes}} {
opacity: 1 !important;
background-color: var(--white);
border: var(--decor-border-100);
border-radius: var(--decor-radius-200);
border-color: var(--gray-500);
box-shadow: var(--decor-elevation-600);
}
{{/if}}
{{#if selected.edges }}
{{selected.edges}} {
opacity: 1;
}
{{/if}}
</style>
<div class="routes">
<header>
<h2>
{{chain.ServiceName}} Router
<span>
<em role="tooltip">Use routers to intercept traffic using L7 criteria such as path prefixes or http headers.</em>
</span>
</h2>
</header>
<div role="group">
{{#each routes as |item|}}
<RouteCard @item={{item}} @onclick={{action "click"}} />
{{/each}}
</div>
</div>
<div class="splitters">
<header>
<h2>
Splitters
<span>
<em role="tooltip">Splitters are configured to split incoming requests across different services or subsets of a single service.</em>
</span>
</h2>
</header>
<div role="group">
{{#each (sort-by 'Name' splitters) as |item|}}
<SplitterCard @item={{item}} @onclick={{action "click"}} />
{{/each}}
</div>
</div>
<div class="resolvers">
<header>
<h2>
Resolvers
<span>
<em role="tooltip">Resolvers are used to define which instances of a service should satisfy discovery requests.</em>
</span>
</h2>
</header>
<div role="group">
{{#each (sort-by 'Name' resolvers) as |item|}}
<ResolverCard @item={{item}} @onclick={{action "click"}} />
{{/each}}
</div>
</div>
<svg width="100%" height="100%" viewBox={{concat '0 0 ' width ' ' height}} preserveAspectRatio="none">
{{#each routes as |item|}}
{{#let (dom-position (concat '#' item.ID)) as |src|}}
{{#let (dom-position (concat '#' item.NextNode)) as |destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat item.ID)) as |dest|}}
<path
id={{concat item.ID '>' item.NextNode}}
d={{
svg-curve (hash
x=dest.x
y=dest.y
) src=(hash
x=(add src.x src.width)
y=(add src.y (div src.height 2))
)}} />
{{/let}}
{{/let}}
{{/let}}
{{/each}}
{{#each splitters as |splitter|}}
{{#let (dom-position (concat '#' splitter.ID)) as |src|}}
{{#each splitter.Splits as |item index|}}
{{#let (dom-position (concat '#' item.NextNode)) as |destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat splitter.ID '-' index)) as |dest|}}
<path
id={{concat 'splitter:' splitter.Name '>' item.NextNode}}
class="split"
data-percentage={{or item.Weight 0}}
d={{
svg-curve (hash
x=dest.x
y=dest.y
) src=(hash
x=(add src.x src.width)
y=(add src.y (div src.height 2))
)}} />
{{/let}}
{{/let}}
{{/each}}
{{/let}}
{{/each}}
</svg>
<svg class="resolver-inlets" viewBox={{concat '0 0 10 ' height}}>
{{#each routes as |item|}}
{{#if (starts-with 'resolver:' item.NextNode) }}
{{#let (dom-position (concat '#' item.NextNode)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/if}}
{{/each}}
{{#each splitters as |item|}}
{{#each item.Splits as |item|}}
{{#let (dom-position (concat '#' item.NextNode)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/each}}
{{/each}}
</svg>
<svg class="splitter-inlets" viewBox={{concat '0 0 10 ' height}}>
{{#each routes as |item|}}
{{#if (starts-with 'splitter:' item.NextNode) }}
{{#let (dom-position (concat '#' item.NextNode)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/if}}
{{/each}}
</svg>
<div class={{concat 'tooltip' (if activeTooltip ' active' '')}} style={{{ concat 'top:' y 'px;left:' x 'px;'}}}>
<span role="tooltip">{{round tooltip decimals=2}}%</span>
</div>
{{/if}}

View File

@ -1,5 +0,0 @@
import Component from '@ember/component';
export default Component.extend({
tagName: '',
});

View File

@ -1,5 +0,0 @@
<a class="splitter-card" onclick={{onclick}} id={{item.ID}}>
<header>
<h3>{{item.Name}}</h3>
</header>
</a>

View File

@ -1,3 +0,0 @@
import Component from '@ember/component';
export default Component.extend({});

View File

@ -29,7 +29,6 @@
@import './components/auth-form';
@import './components/auth-modal';
@import './components/oidc-select';
@import './components/discovery-chain';
@import './components/empty-state';
@import './components/tabular-details';
@import './components/tabular-collection';
@ -56,6 +55,7 @@
@import 'consul-ui/components/notice';
@import 'consul-ui/components/modal-dialog';
@import 'consul-ui/components/consul/discovery-chain';
@import 'consul-ui/components/consul/exposed-path/list';
@import 'consul-ui/components/consul/external-source';
@import 'consul-ui/components/consul/kind';

View File

@ -1,2 +0,0 @@
@import './skin';
@import './layout';

View File

@ -1,6 +1,8 @@
<div id="routing" class="tab-section">
<div role="tabpanel">
<DiscoveryChain @chain={{chain.Chain}} />
<Consul::DiscoveryChain
@chain={{chain.Chain}}
/>
</div>
</div>

View File

@ -1,7 +1,7 @@
import { getAlternateServices } from 'consul-ui/components/discovery-chain/utils';
import { getAlternateServices } from 'consul-ui/components/consul/discovery-chain/utils';
import { module, test } from 'qunit';
module('Unit | Component | discovery-chain/get-alternative-services', function() {
module('Unit | Component | consul/discovery-chain/get-alternative-services', function() {
test('it guesses a different namespace', function(assert) {
const expected = {
Type: 'Namespace',

View File

@ -1,4 +1,4 @@
import { getResolvers } from 'consul-ui/components/discovery-chain/utils';
import { getResolvers } from 'consul-ui/components/consul/discovery-chain/utils';
import { module, test } from 'qunit';
import { get } from 'consul-ui/tests/helpers/api';
@ -7,7 +7,7 @@ const nspace = 'default';
const request = {
url: `/v1/discovery-chain/service-name?dc=${dc}`,
};
module('Unit | Component | discovery-chain/get-resolvers', function() {
module('Unit | Component | consul/discovery-chain/get-resolvers', function() {
test('it assigns Subsets correctly', function(assert) {
return get(request.url, {
headers: {

View File

@ -1,7 +1,7 @@
import { getSplitters } from 'consul-ui/components/discovery-chain/utils';
import { getSplitters } from 'consul-ui/components/consul/discovery-chain/utils';
import { module, test } from 'qunit';
module('Unit | Component | discovery-chain/get-splitters', function() {
module('Unit | Component | consul/discovery-chain/get-splitters', function() {
test('it collects and correctly parses splitter Names', function(assert) {
const actual = getSplitters({
'splitter:splitter-name.default': {