ui: Redesign - Service Detail Page (#7655)
* Create ConsulServiceInstanceList with styling and test * Implement ConsulServiceInstanceList to Service Detail page * Implement ConsulExternalSource to Services Show page header * Update services/show page object * Update the styling of CompositeRow * Refactor ConsulServiceList component and styling * Update ConsulExternalSource to say 'Registered via...' * Upgrade consul-api-double to patch 2.14.1 * Fix up tests to not use Kind in service models * Update ListCollection with clickFirstAnchor action * Add $typo-size-450 to typography base variables
This commit is contained in:
parent
455dfe0a1b
commit
958991db94
|
@ -1,17 +1,19 @@
|
|||
{{#let (if _externalSource _externalSource (service/external-source item)) as |externalSource|}}
|
||||
{{#if externalSource}}
|
||||
{{#if (has-block)}}
|
||||
{{yield
|
||||
(component 'consul-external-source' item=item _externalSource=externalSource)
|
||||
}}
|
||||
{{else}}
|
||||
<span data-test-external-source={{externalSource}} class="consul-external-source source-{{externalSource}}">
|
||||
{{#if (eq externalSource 'aws')}}
|
||||
<span>Synced from {{uppercase externalSource}}</span>
|
||||
{{else}}
|
||||
<span>Synced from {{capitalize externalSource}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{#if item}}
|
||||
{{#let (if _externalSource _externalSource (service/external-source item)) as |externalSource|}}
|
||||
{{#if externalSource}}
|
||||
{{#if (has-block)}}
|
||||
{{yield
|
||||
(component 'consul-external-source' item=item _externalSource=externalSource)
|
||||
}}
|
||||
{{else}}
|
||||
<span data-test-external-source={{externalSource}} class="consul-external-source source-{{externalSource}}">
|
||||
{{#if (eq externalSource 'aws')}}
|
||||
<span>Registered via {{uppercase externalSource}}</span>
|
||||
{{else}}
|
||||
<span>Registered via {{capitalize externalSource}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
{{yield}}
|
||||
{{#if (gt items.length 0)}}
|
||||
<ListCollection @cellHeight={{73}} @items={{items}} class="consul-service-instance-list" as |item index|>
|
||||
<a href={{href-to routeName item.Service.Service item.Node.Node (or item.Service.ID item.Service.Service)}}>
|
||||
{{item.Service.ID}}
|
||||
</a>
|
||||
<ul>
|
||||
<ConsulExternalSource @item={{item.Service}} as |ExternalSource|>
|
||||
<li>
|
||||
<ExternalSource />
|
||||
</li>
|
||||
</ConsulExternalSource>
|
||||
{{#with (reject-by 'ServiceID' '' item.Checks) as |checks|}}
|
||||
<li class={{service/instance-checks checks}}>
|
||||
{{checks.length}} service checks
|
||||
</li>
|
||||
{{/with}}
|
||||
{{#with (filter-by 'ServiceID' '' item.Checks) as |checks|}}
|
||||
<li class={{service/instance-checks checks}}>
|
||||
{{checks.length}} node checks
|
||||
</li>
|
||||
{{/with}}
|
||||
{{#if (get proxies item.Service.ID)}}
|
||||
<li class="proxy">
|
||||
connected with proxy
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (gt item.Node.Node.length 0)}}
|
||||
<li class="node">
|
||||
<a href={{href-to 'dc.nodes.show' item.Node.Node}}>{{item.Node.Node}}</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li class="address" data-test-address>
|
||||
{{#if (not-eq item.Service.Address '')}}
|
||||
{{item.Service.Address}}:{{item.Service.Port}}
|
||||
{{else}}
|
||||
{{item.Node.Address}}:{{item.Service.Port}}
|
||||
{{/if}}
|
||||
</li>
|
||||
{{#if (gt item.Service.Tags.length 0)}}
|
||||
<li>
|
||||
<TagList @items={{item.Service.Tags}}/>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</ListCollection>
|
||||
{{/if}}
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -1,14 +1,30 @@
|
|||
{{yield}}
|
||||
{{#if (gt items.length 0)}}
|
||||
<ListCollection @cellHeight={{73}} @items={{items}} class="consul-service-list" as |item index|>
|
||||
<a href={{href-to routeName item.Name}}>
|
||||
<span class={{service/health-checks item}}></span>
|
||||
<span>
|
||||
{{item.Name}}
|
||||
</span>
|
||||
<YieldSlot @name="metadata" @params={{block-params item}}>
|
||||
{{yield}}
|
||||
</YieldSlot>
|
||||
<a href={{href-to routeName item.Name}} class={{service/health-checks item}}>
|
||||
{{item.Name}}
|
||||
</a>
|
||||
<ul>
|
||||
<ConsulExternalSource @item={{item}} as |ExternalSource|>
|
||||
<li>
|
||||
<ExternalSource />
|
||||
</li>
|
||||
</ConsulExternalSource>
|
||||
{{#if (not-eq item.InstanceCount 0)}}
|
||||
<li>
|
||||
{{format-number item.InstanceCount}} {{pluralize item.InstanceCount 'Instance' without-count=true}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (get proxies item.Name)}}
|
||||
<li class="proxy">
|
||||
connected with proxy
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (gt items.Tags.length 0)}}
|
||||
<li>
|
||||
<TagList @items={{item.Tags}}/>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</ListCollection>
|
||||
{{/if}}
|
|
@ -1,6 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
import Slotted from 'block-slots';
|
||||
|
||||
export default Component.extend(Slotted, {
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<EmberNativeScrollable @tagName="ul" @content-size={{_contentSize}} @scroll-left={{_scrollLeft}} @scroll-top={{_scrollTop}} @scrollChange={{action "scrollChange"}} @clientSizeChange={{action "clientSizeChange"}}>
|
||||
<li></li>
|
||||
{{~#each _cells as |cell|~}}
|
||||
<li style={{{cell.style}}}>{{yield cell.item cell.index }}</li>
|
||||
<li onclick={{action 'click'}} style={{{cell.style}}}>{{yield cell.item cell.index }}</li>
|
||||
{{~/each~}}
|
||||
</EmberNativeScrollable>
|
|
@ -45,4 +45,9 @@ export default Component.extend(WithResizing, {
|
|||
this.updateScrollPosition();
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
click: function(e) {
|
||||
return this.dom.clickFirstAnchor(e, 'li');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -21,19 +21,19 @@ export default Controller.extend(WithEventSource, WithSearching, {
|
|||
}),
|
||||
services: computed('items.[]', function() {
|
||||
return this.items.filter(function(item) {
|
||||
return item.Kind === 'consul';
|
||||
return typeof item.Kind === 'undefined';
|
||||
});
|
||||
}),
|
||||
proxies: computed('items.[]', function() {
|
||||
return this.items.filter(function(item) {
|
||||
return item.Kind === 'connect-proxy';
|
||||
});
|
||||
}),
|
||||
withProxies: computed('proxies', function() {
|
||||
const proxies = {};
|
||||
this.proxies.forEach(item => {
|
||||
proxies[item.Name.replace('-proxy', '')] = true;
|
||||
});
|
||||
this.items
|
||||
.filter(function(item) {
|
||||
return item.Kind === 'connect-proxy';
|
||||
})
|
||||
.forEach(item => {
|
||||
proxies[item.Name.replace('-proxy', '')] = true;
|
||||
});
|
||||
|
||||
return proxies;
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import { get, computed } from '@ember/object';
|
|||
import { alias } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
|
||||
export default Controller.extend(WithSearching, {
|
||||
dom: service('dom'),
|
||||
items: alias('item.Nodes'),
|
||||
|
@ -23,4 +24,12 @@ export default Controller.extend(WithSearching, {
|
|||
.add(this.items)
|
||||
.search(get(this, this.searchParams.serviceInstance));
|
||||
}),
|
||||
keyedProxies: computed('proxies.[]', function() {
|
||||
const proxies = {};
|
||||
this.proxies.forEach(item => {
|
||||
proxies[item.ServiceProxy.DestinationServiceID] = true;
|
||||
});
|
||||
|
||||
return proxies;
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export function healthChecks([items], hash) {
|
||||
let ChecksCritical = 0;
|
||||
let ChecksWarning = 0;
|
||||
let ChecksPassing = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
switch (item.Status) {
|
||||
case 'critical':
|
||||
ChecksCritical += 1;
|
||||
break;
|
||||
case 'warning':
|
||||
ChecksWarning += 1;
|
||||
break;
|
||||
case 'passing':
|
||||
ChecksPassing += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
switch (true) {
|
||||
case ChecksCritical !== 0:
|
||||
return 'critical';
|
||||
case ChecksWarning !== 0:
|
||||
return 'warning';
|
||||
case ChecksPassing !== 0:
|
||||
return 'passing';
|
||||
default:
|
||||
return 'empty';
|
||||
}
|
||||
}
|
||||
|
||||
export default helper(healthChecks);
|
|
@ -7,6 +7,7 @@ export default Route.extend({
|
|||
repo: service('repository/service'),
|
||||
intentionRepo: service('repository/intention'),
|
||||
chainRepo: service('repository/discovery-chain'),
|
||||
proxyRepo: service('repository/proxy'),
|
||||
settings: service('settings'),
|
||||
model: function(params, transition = {}) {
|
||||
const dc = this.modelFor('dc').dc.Name;
|
||||
|
@ -16,12 +17,11 @@ export default Route.extend({
|
|||
intentions: this.intentionRepo.findByService(params.name, dc, nspace),
|
||||
urls: this.settings.findBySlug('urls'),
|
||||
dc: dc,
|
||||
nspace: nspace,
|
||||
}).then(model => {
|
||||
return hash({
|
||||
chain: ['connect-proxy', 'mesh-gateway'].includes(get(model, 'item.Service.Kind'))
|
||||
? null
|
||||
: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) {
|
||||
return ['connect-proxy', 'mesh-gateway'].includes(get(model, 'item.Service.Kind'))
|
||||
? model
|
||||
: hash({
|
||||
chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) {
|
||||
const code = get(e, 'errors.firstObject.status');
|
||||
// Currently we are specifically catching a 500, but we return null
|
||||
// by default, so null for all errors.
|
||||
|
@ -38,8 +38,9 @@ export default Route.extend({
|
|||
return null;
|
||||
}
|
||||
}),
|
||||
...model,
|
||||
});
|
||||
proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace),
|
||||
...model,
|
||||
});
|
||||
});
|
||||
},
|
||||
setupController: function(controller, model) {
|
||||
|
|
|
@ -6,6 +6,7 @@ $typo-size-100: 3.5rem;
|
|||
$typo-size-200: 1.8rem;
|
||||
$typo-size-300: 1.3rem;
|
||||
$typo-size-400: 1.2rem;
|
||||
$typo-size-450: 1.125rem;
|
||||
$typo-size-500: 1rem;
|
||||
$typo-size-600: 0.875rem;
|
||||
$typo-size-700: 0.8125rem;
|
||||
|
|
|
@ -115,4 +115,11 @@ main {
|
|||
#toolbar-toggle:checked + * {
|
||||
display: block;
|
||||
}
|
||||
html.template-service.template-show #toolbar-toggle + * {
|
||||
display: block;
|
||||
padding: 4px;
|
||||
}
|
||||
html.template-service.template-show .actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,16 @@
|
|||
%app-view-header dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
%app-view-header .title-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
%app-view-header .title-bar > h1 {
|
||||
border: 0;
|
||||
}
|
||||
%app-view-header .title-bar > span {
|
||||
margin-left: 8px;
|
||||
}
|
||||
/* units */
|
||||
%app-view {
|
||||
margin-top: 50px;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
@import './layout';
|
||||
@import './skin';
|
||||
%composite-row a:hover,
|
||||
%composite-row a:focus,
|
||||
%composite-row a:active {
|
||||
%composite-row:hover,
|
||||
%composite-row:focus,
|
||||
%composite-row:active {
|
||||
@extend %composite-row-intent;
|
||||
}
|
||||
%composite-row > a > span {
|
||||
%composite-row > a {
|
||||
@extend %composite-row-header;
|
||||
}
|
||||
%composite-row > a > ul {
|
||||
%composite-row > ul {
|
||||
@extend %composite-row-detail;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
%composite-row a {
|
||||
%composite-row {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
|
@ -19,3 +20,6 @@
|
|||
%composite-row-detail > li:not(:first-child) {
|
||||
margin-left: 12px;
|
||||
}
|
||||
%composite-row-detail .node::before {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
%composite-row {
|
||||
list-style-type: none;
|
||||
}
|
||||
%composite-row a {
|
||||
border-top-color: $gray-200;
|
||||
border-bottom-color: $gray-200;
|
||||
border-bottom-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
@ -17,3 +15,41 @@
|
|||
%composite-row-detail {
|
||||
color: $gray-500;
|
||||
}
|
||||
%composite-row:last-child {
|
||||
border-bottom-color: $gray-200;
|
||||
}
|
||||
|
||||
// Health Checks
|
||||
%composite-row .passing::before {
|
||||
@extend %with-check-circle-fill-color-mask, %as-pseudo;
|
||||
background-color: $green-500;
|
||||
}
|
||||
%composite-row .warning::before {
|
||||
@extend %with-alert-triangle-color-mask, %as-pseudo;
|
||||
background-color: $orange-500;
|
||||
}
|
||||
%composite-row .critical::before {
|
||||
@extend %with-cancel-square-fill-color-mask, %as-pseudo;
|
||||
background-color: $red-500;
|
||||
}
|
||||
|
||||
// Metadata
|
||||
%composite-row .node a {
|
||||
color: $gray-500;
|
||||
}
|
||||
%composite-row .node a:hover {
|
||||
color: $color-action;
|
||||
text-decoration: underline;
|
||||
}
|
||||
%composite-row .node::before {
|
||||
@extend %with-git-commit-mask, %as-pseudo;
|
||||
background-color: $gray-500;
|
||||
}
|
||||
%composite-row .address::before {
|
||||
@extend %with-public-default-mask, %as-pseudo;
|
||||
background-color: $gray-500;
|
||||
}
|
||||
%composite-row .proxy::before {
|
||||
@extend %with-swap-horizontal-mask, %as-pseudo;
|
||||
background-color: $gray-500;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
.consul-external-source {
|
||||
background-color: $gray-100;
|
||||
padding: 0 8px;
|
||||
height: 16px;
|
||||
line-height: 12px;
|
||||
margin-top: 3px;
|
||||
border-radius: $decor-radius-100;
|
||||
height: 18px;
|
||||
line-height: 0.7rem;
|
||||
}
|
||||
.consul-external-source > span {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
color: $gray-500;
|
||||
}
|
||||
.consul-external-source::before {
|
||||
font-size: 0.7em;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 2px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
.consul-service-instance-list > ul {
|
||||
@extend %consul-service-instance-list;
|
||||
}
|
||||
%consul-service-instance-list > li:not(:first-child) {
|
||||
@extend %consul-service-instance-row;
|
||||
}
|
||||
%consul-service-instance-row {
|
||||
@extend %composite-row;
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
@import './consul-service-list/index';
|
||||
|
||||
.consul-service-list > ul {
|
||||
@extend %consul-service-list;
|
||||
}
|
||||
|
@ -9,3 +7,6 @@
|
|||
%consul-service-row {
|
||||
@extend %composite-row;
|
||||
}
|
||||
%consul-service-row > ul {
|
||||
margin-left: 26px;
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
@import './layout';
|
||||
@import './skin';
|
|
@ -1,16 +0,0 @@
|
|||
%consul-service-row > a > span:first-child {
|
||||
margin-right: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
%consul-service-row > a > span:nth-child(3) {
|
||||
margin-left: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
%consul-service-row > a > ul {
|
||||
margin-left: 26px;
|
||||
}
|
||||
%consul-service-row .proxy::before {
|
||||
margin-right: 4px;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
%consul-service-row > a > span:nth-child(2) {
|
||||
font-size: 1.125rem;
|
||||
font-weight: $typo-weight-medium;
|
||||
}
|
||||
%consul-service-row > a > span:first-child,
|
||||
%consul-service-row > a > span:nth-child(3) {
|
||||
@extend %as-pseudo;
|
||||
}
|
||||
|
||||
// Health Checks
|
||||
%consul-service-row .empty {
|
||||
@extend %with-minus-square-fill-mask;
|
||||
background-color: #7c8797;
|
||||
}
|
||||
%consul-service-row .passing {
|
||||
@extend %with-check-circle-fill-mask;
|
||||
background-color: $green-500;
|
||||
}
|
||||
%consul-service-row .warning {
|
||||
@extend %with-alert-triangle-mask;
|
||||
background-color: $orange-500;
|
||||
}
|
||||
%consul-service-row .critical {
|
||||
@extend %with-cancel-square-fill-mask;
|
||||
background-color: $red-500;
|
||||
}
|
||||
%consul-service-row .proxy::before {
|
||||
@extend %with-git-commit-mask, %as-pseudo;
|
||||
background-color: $gray-500;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
%filter-bar {
|
||||
padding: 14px;
|
||||
padding: 4px;
|
||||
display: block;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
@media #{$--horizontal-filters} {
|
||||
%filter-bar {
|
||||
|
@ -19,6 +20,6 @@
|
|||
}
|
||||
@media #{$--lt-horizontal-filters} {
|
||||
%filter-bar > *:first-child {
|
||||
margin-bottom: 12px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
@import './list-collection';
|
||||
@import './grid-collection';
|
||||
@import './consul-service-list';
|
||||
@import './consul-service-instance-list';
|
||||
|
||||
/**/
|
||||
|
||||
|
|
|
@ -109,6 +109,13 @@ pre code,
|
|||
}
|
||||
/**/
|
||||
|
||||
/* composite row */
|
||||
%composite-row-header {
|
||||
font-size: $typo-size-450;
|
||||
font-weight: $typo-weight-medium;
|
||||
}
|
||||
/**/
|
||||
|
||||
/* TODO: We have nothing else with a 500 */
|
||||
/* either make this the biggest %p or don't use it */
|
||||
%app-view h1 em {
|
||||
|
|
|
@ -14,21 +14,7 @@
|
|||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}}>
|
||||
<BlockSlot @name="metadata" as |item|>
|
||||
<ul>
|
||||
<ConsulExternalSource @item={{item}} as |ExternalSource|>
|
||||
<li>
|
||||
<ExternalSource />
|
||||
</li>
|
||||
</ConsulExternalSource>
|
||||
<li>{{format-number item.InstanceCount}} {{pluralize item.InstanceCount 'Instance' without-count=true}}</li>
|
||||
{{#if (get withProxies item.Name)}}<li class="proxy">connected with proxy</li>{{/if}}
|
||||
<li><TagList @items={{item.Tags}}/></li>
|
||||
</ul>
|
||||
</BlockSlot>
|
||||
</ConsulServiceList>
|
||||
</BlockSlot>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}} @proxies={{proxies}}/> </BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
There are no services.
|
||||
|
|
|
@ -10,29 +10,21 @@
|
|||
</ol>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
{{item.Service.Service}}
|
||||
{{#let (service/external-source item.Service) as |externalSource| }}
|
||||
{{#if externalSource }}
|
||||
<span data-test-external-source={{externalSource}} style={{concat 'background-image: var(--' externalSource '-icon)'}} data-tooltip="Registered via {{externalSource}}">Registered via {{externalSource}}</span>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{#if (eq item.Service.Kind 'connect-proxy')}}
|
||||
<span class="kind-proxy">Proxy</span>
|
||||
{{else if (eq item.Service.Kind 'mesh-gateway')}}
|
||||
<span class="kind-proxy">Mesh Gateway</span>
|
||||
{{/if}}
|
||||
</h1>
|
||||
<label for="toolbar-toggle"></label>
|
||||
<TabNav @items={{
|
||||
compact
|
||||
(array
|
||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
||||
<div class="title-bar">
|
||||
<h1>
|
||||
{{item.Service.Service}}
|
||||
</h1>
|
||||
<ConsulExternalSource @item={{item.Service}} />
|
||||
</div>
|
||||
<TabNav @items={{
|
||||
compact
|
||||
(array
|
||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
||||
(if (not-eq chain) (hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing")) '')
|
||||
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
||||
)
|
||||
}}/>
|
||||
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
||||
)
|
||||
}}/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
{{#if urls.service}}
|
||||
|
|
|
@ -8,48 +8,7 @@
|
|||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection
|
||||
data-test-instances
|
||||
@items={{filtered}} as |item index|
|
||||
>
|
||||
<BlockSlot @name="header">
|
||||
<th>ID</th>
|
||||
<th>Node</th>
|
||||
<th>Address</th>
|
||||
<th>Node Checks</th>
|
||||
<th>Service Checks</th>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="row">
|
||||
<td data-test-id={{item.Service.ID}}>
|
||||
<a href={{href-to 'dc.services.instance' item.Service.Service item.Node.Node (or item.Service.ID item.Service.Service)}}>
|
||||
{{#let (service/external-source item.Service) as |externalSource| }}
|
||||
{{#if externalSource }}
|
||||
<span data-test-external-source={{externalSource}} style={{concat 'background-image: var(--' externalSource '-icon)'}}></span>
|
||||
{{else}}
|
||||
<span></span>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{or item.Service.ID item.Service.Service}}
|
||||
</a>
|
||||
</td>
|
||||
<td data-test-node>
|
||||
<a href={{href-to 'dc.nodes.show' item.Node.Node}}>{{item.Node.Node}}</a>
|
||||
</td>
|
||||
<td data-test-address>
|
||||
{{item.Service.Address}}:{{item.Service.Port}}
|
||||
</td>
|
||||
<td>
|
||||
{{#with (reject-by 'ServiceID' '' item.Checks) as |checks|}}
|
||||
<HealthcheckInfo @passing={{filter-by "Status" "passing" checks}} @warning={{filter-by "Status" "warning" checks}} @critical={{filter-by "Status" "critical" checks}} />
|
||||
{{/with}}
|
||||
</td>
|
||||
<td>
|
||||
{{#with (filter-by 'ServiceID' '' item.Checks) as |checks|}}
|
||||
<HealthcheckInfo @passing={{filter-by "Status" "passing" checks}} @warning={{filter-by "Status" "warning" checks}} @critical={{filter-by "Status" "critical" checks}} />
|
||||
{{/with}}
|
||||
</td>
|
||||
</BlockSlot>
|
||||
</TabularCollection>
|
||||
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{filtered}} @proxies={{keyedProxies}}/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
|
|
|
@ -14,7 +14,11 @@ const clickEvent = function($el) {
|
|||
export default function(closest, click = clickEvent) {
|
||||
// TODO: Decide whether we should use `e` for ease
|
||||
// or `target`/`el`
|
||||
return function(e) {
|
||||
// TODO: currently, using a string stopElement to tell the func
|
||||
// where to stop looking and currenlty default is 'tr' because
|
||||
// it's backwards compatible.
|
||||
// Long-term this func shouldn't default to 'tr'
|
||||
return function(e, stopElement = 'tr') {
|
||||
// click on row functionality
|
||||
// so if you click the actual row but not a link
|
||||
// find the first link and fire that instead
|
||||
|
@ -26,9 +30,7 @@ export default function(closest, click = clickEvent) {
|
|||
case 'button':
|
||||
return;
|
||||
}
|
||||
// TODO: why should this be restricted to a tr
|
||||
// closest should probably be relaced with a finder function
|
||||
const $a = closest('tr', e.target).querySelector('a');
|
||||
const $a = closest(stopElement, e.target).querySelector('a');
|
||||
if ($a) {
|
||||
click($a);
|
||||
}
|
||||
|
|
|
@ -127,26 +127,30 @@ Feature: components / catalog-filter
|
|||
-------------------------------------------------
|
||||
Scenario: Freetext filtering the service listing
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 service models from yaml
|
||||
And 6 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
Kind: consul
|
||||
Tags: ['one', 'two', 'three']
|
||||
ChecksPassing: 0
|
||||
ChecksWarning: 0
|
||||
ChecksCritical: 1
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
Kind: consul
|
||||
Tags: ['two', 'three']
|
||||
ChecksPassing: 0
|
||||
ChecksWarning: 1
|
||||
ChecksCritical: 0
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
Kind: consul
|
||||
Tags: ['three']
|
||||
ChecksPassing: 1
|
||||
ChecksWarning: 0
|
||||
ChecksCritical: 0
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
|
||||
---
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
|
|
|
@ -6,14 +6,17 @@ Feature: dc / error: Recovering from a dc 500 error
|
|||
- dc-1
|
||||
- dc-500
|
||||
---
|
||||
And 3 service models from yaml
|
||||
And 6 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
Kind: consul
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
Kind: consul
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
Kind: consul
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
---
|
||||
And the url "/v1/internal/ui/services" responds with a 500 status
|
||||
When I visit the services page for yaml
|
||||
|
|
|
@ -32,7 +32,7 @@ Feature: dc / list-blocking
|
|||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
service: service-0-proxy
|
||||
service: service
|
||||
---
|
||||
Then the url should be /dc-1/[Url]
|
||||
And pause until I see 3 [Model] models
|
||||
|
@ -45,5 +45,5 @@ Feature: dc / list-blocking
|
|||
Where:
|
||||
-----------------------------------------------------------------
|
||||
| Page | Model | Url |
|
||||
| service | instance | services/service-0-proxy/instances |
|
||||
| service | instance | services/service/instances |
|
||||
-----------------------------------------------------------------
|
||||
|
|
|
@ -13,20 +13,26 @@ Feature: dc / nspaces / manage : Managing Namespaces
|
|||
---
|
||||
- dc-1
|
||||
---
|
||||
And 6 service models from yaml
|
||||
And 12 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
Kind: consul
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
Kind: consul
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
Kind: consul
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-3
|
||||
Kind: consul
|
||||
- Name: Service-3-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-4
|
||||
Kind: consul
|
||||
- Name: Service-4-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-5
|
||||
Kind: consul
|
||||
- Name: Service-5-proxy
|
||||
Kind: 'connect-proxy'
|
||||
---
|
||||
|
||||
When I visit the services page for yaml
|
||||
|
|
|
@ -6,21 +6,28 @@ Feature: dc / services / dc-switch : Switching Datacenters
|
|||
- dc-1
|
||||
- dc-2
|
||||
---
|
||||
And 6 service models from yaml
|
||||
And 12 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
Kind: consul
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
Kind: consul
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
Kind: consul
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-3
|
||||
Kind: consul
|
||||
- Name: Service-3-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-4
|
||||
Kind: consul
|
||||
- Name: Service-4-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-5
|
||||
Kind: consul
|
||||
- Name: Service-5-proxy
|
||||
Kind: 'connect-proxy'
|
||||
---
|
||||
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
|
|
|
@ -2,36 +2,42 @@
|
|||
Feature: dc / services / index: List Services
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 4 service models from yaml
|
||||
And 10 service models from yaml
|
||||
---
|
||||
- Name: Service 1
|
||||
Kind: consul
|
||||
ExternalSources:
|
||||
- consul
|
||||
- Name: Service 2
|
||||
Kind: consul
|
||||
ExternalSources:
|
||||
- nomad
|
||||
- Name: Service 3
|
||||
Kind: consul
|
||||
ExternalSources:
|
||||
- terraform
|
||||
- Name: Service 4
|
||||
Kind: consul
|
||||
ExternalSources:
|
||||
- kubernetes
|
||||
- Name: Service 5
|
||||
Kind: consul
|
||||
ExternalSources:
|
||||
- aws
|
||||
- Name: Service-0
|
||||
ExternalSources:
|
||||
- consul
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
ExternalSources:
|
||||
- nomad
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
ExternalSources:
|
||||
- terraform
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-3
|
||||
ExternalSources:
|
||||
- kubernetes
|
||||
- Name: Service-3-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-4
|
||||
ExternalSources:
|
||||
- aws
|
||||
- Name: Service-4-proxy
|
||||
Kind: 'connect-proxy'
|
||||
---
|
||||
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/services
|
||||
And the title should be "Services - Consul"
|
||||
Then I see 4 service models
|
||||
Then I see 5 service models
|
||||
And I see externalSource on the services like yaml
|
||||
---
|
||||
- consul
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
Feature: dc / services / list blocking
|
||||
Scenario: Viewing the listing pages for service
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
Given 3 service models from yaml
|
||||
And 6 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
Kind: consul
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
Kind: consul
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
Kind: consul
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
---
|
||||
And a network latency of 100
|
||||
When I visit the services page for yaml
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
Feature: dc / services / list
|
||||
Scenario: Listing service
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 service models from yaml
|
||||
And 6 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
Kind: consul
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-1
|
||||
Kind: consul
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
- Name: Service-2
|
||||
Kind: consul
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
---
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
|
|
|
@ -6,7 +6,6 @@ Feature: dc / services / Show Routing for Service
|
|||
And 1 service model from yaml
|
||||
---
|
||||
- Service:
|
||||
Kind: consul
|
||||
Name: service-0
|
||||
ID: service-0-with-id
|
||||
---
|
||||
|
@ -17,31 +16,12 @@ Feature: dc / services / Show Routing for Service
|
|||
---
|
||||
And the title should be "service-0 - Consul"
|
||||
And I see routing on the tabs
|
||||
Scenario: Given a service proxy, the Routing tab should not display
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 1 node models
|
||||
And 1 service model from yaml
|
||||
---
|
||||
- Service:
|
||||
Kind: connect-proxy
|
||||
Name: service-0-proxy
|
||||
ID: service-0-proxy-with-id
|
||||
---
|
||||
When I visit the service page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
service: service-0-proxy
|
||||
---
|
||||
And the title should be "service-0-proxy - Consul"
|
||||
And I don't see routing on the tabs
|
||||
|
||||
Scenario: Given connect is disabled, the Routing tab should not display or error
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 1 node models
|
||||
And 1 service model from yaml
|
||||
---
|
||||
- Service:
|
||||
Kind: consul
|
||||
Name: service-0
|
||||
ID: service-0-with-id
|
||||
---
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { module, skip } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | consul-service-instance-list', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
skip('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<ConsulServiceInstanceList />`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<ConsulServiceInstanceList>
|
||||
template block text
|
||||
</ConsulServiceInstanceList>
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'template block text');
|
||||
});
|
||||
});
|
|
@ -78,16 +78,7 @@ export default {
|
|||
services(visitable, clickable, text, attribute, collection, page, catalogFilter, radiogroup)
|
||||
),
|
||||
service: create(
|
||||
service(
|
||||
visitable,
|
||||
clickable,
|
||||
attribute,
|
||||
collection,
|
||||
text,
|
||||
consulIntentionList,
|
||||
catalogFilter,
|
||||
tabgroup
|
||||
)
|
||||
service(visitable, attribute, collection, text, consulIntentionList, catalogFilter, tabgroup)
|
||||
),
|
||||
instance: create(instance(visitable, attribute, collection, text, tabgroup)),
|
||||
nodes: create(nodes(visitable, clickable, attribute, collection, catalogFilter)),
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
export default function(
|
||||
visitable,
|
||||
clickable,
|
||||
attribute,
|
||||
collection,
|
||||
text,
|
||||
intentions,
|
||||
filter,
|
||||
tabs
|
||||
) {
|
||||
export default function(visitable, attribute, collection, text, intentions, filter, tabs) {
|
||||
return {
|
||||
visit: visitable('/:dc/services/:service'),
|
||||
externalSource: attribute('data-test-external-source', 'h1 span'),
|
||||
externalSource: attribute('data-test-external-source', '[data-test-external-source]', {
|
||||
scope: '.title-bar',
|
||||
}),
|
||||
dashboardAnchor: {
|
||||
href: attribute('href', '[data-test-dashboard-anchor]'),
|
||||
},
|
||||
|
@ -18,7 +11,7 @@ export default function(
|
|||
filter: filter,
|
||||
|
||||
// TODO: These need to somehow move to subpages
|
||||
instances: collection('#instances [data-test-tabular-row]', {
|
||||
instances: collection('.consul-service-instance-list > ul > li:not(:first-child)', {
|
||||
address: text('[data-test-address]'),
|
||||
}),
|
||||
intentions: intentions(),
|
||||
|
|
|
@ -1211,9 +1211,9 @@
|
|||
js-yaml "^3.13.1"
|
||||
|
||||
"@hashicorp/consul-api-double@^2.6.2":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.14.0.tgz#ecef725fc22490011a671bc0a285a16013ca5e53"
|
||||
integrity sha512-1rGMg/XSHR2ROr8a7OVEwOUy8UWuYdNUMijMxCuFHR201vDAGK9EDmkJCPF2PfYsDrcsiyb/0dxIL6Mba9p32Q==
|
||||
version "2.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.14.1.tgz#c4beefa853368319324a6092fc4eb4371f9f8ffc"
|
||||
integrity sha512-ZJtjkAuFHqcLTRjVaqx4NYnkZ17v5/DjaDhjH/kRVBx0gXcyKUEeMe34g69PfqgRo6ZYMVYMbSDq0JHTGcIShQ==
|
||||
|
||||
"@hashicorp/ember-cli-api-double@^3.0.2":
|
||||
version "3.0.2"
|
||||
|
|
Loading…
Reference in New Issue