mirror of https://github.com/status-im/consul.git
Merge pull request #8080 from hashicorp/ui-staging
ui: UI Release Merge (1.8-beta-3: ui-staging merge)
This commit is contained in:
parent
d7d37bace4
commit
daf283d315
|
@ -1,11 +1,93 @@
|
|||
{{#if item.Kind}}
|
||||
{{#if (has-block)}}
|
||||
{{yield
|
||||
(component 'consul-kind' item=item)
|
||||
}}
|
||||
{{else}}
|
||||
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
|
||||
<span>{{titleize (humanize item.Kind)}}</span>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#let (titleize (humanize item.Kind)) as |Name|}}
|
||||
{{#if (has-block)}}
|
||||
{{yield
|
||||
(component 'consul-kind' item=item withInfo=withInfo)
|
||||
}}
|
||||
{{else}}
|
||||
{{#if withInfo}}
|
||||
<dl class="tooltip-panel">
|
||||
<dt>
|
||||
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
|
||||
<span>{{Name}}</span>
|
||||
</span>
|
||||
</dt>
|
||||
<dd>
|
||||
<MenuPanel @position="left">
|
||||
<BlockSlot @name="header">
|
||||
{{#if (eq item.Kind 'ingress-gateway')}}
|
||||
Ingress gateways enable ingress traffic from services outside the Consul service mesh to services inside the Consul service mesh.
|
||||
{{else if (eq item.Kind 'terminating-gateway')}}
|
||||
Terminating gateways allow connect-enabled services in Consul service mesh to communicate with services outside the service mesh.
|
||||
{{else}}
|
||||
Mesh gateways enable routing of Connect traffic between different Consul datacenters.
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu">
|
||||
<li role="separator">
|
||||
{{#if (eq item.Kind 'ingress-gateway')}}
|
||||
About Ingress gateways
|
||||
{{else if (eq item.Kind 'terminating-gateway')}}
|
||||
About Terminating gateways
|
||||
{{else}}
|
||||
About Mesh gateways
|
||||
{{/if}}
|
||||
</li>
|
||||
{{#let (from-entries
|
||||
(array 'ingress-gateway' '/consul/developer-mesh/ingress-gateways')
|
||||
(array 'terminating-gateway' '/consul/developer-mesh/understand-terminating-gateways')
|
||||
(array 'mesh-gateway' '/consul/developer-mesh/connect-gateways')
|
||||
) as |link|}}
|
||||
<li role="none" class="learn-link">
|
||||
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_LEARN_URL') (get link item.Kind)}} rel="noopener noreferrer" target="_blank">
|
||||
Learn guides
|
||||
</a>
|
||||
</li>
|
||||
{{/let}}
|
||||
{{#let (from-entries
|
||||
(array 'ingress-gateway' '/connect/ingress_gateway.html')
|
||||
(array 'terminating-gateway' '/connect/terminating_gateway.html')
|
||||
(array 'mesh-gateway' '/connect/mesh_gateway.html')
|
||||
) as |link|}}
|
||||
<li role="none" class="docs-link">
|
||||
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link item.Kind)}} rel="noopener noreferrer" target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</li>
|
||||
<li role="separator">
|
||||
Other gateway types
|
||||
</li>
|
||||
{{#if (not-eq item.Kind 'mesh-gateway')}}
|
||||
<li role="none" class="docs-link">
|
||||
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'mesh-gateway')}} rel="noopener noreferrer" target="_blank">
|
||||
Mesh gateways
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (not-eq item.Kind 'terminating-gateway')}}
|
||||
<li role="none" class="docs-link">
|
||||
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'terminating-gateway')}} rel="noopener noreferrer" target="_blank">
|
||||
Terminating gateways
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (not-eq item.Kind 'ingress-gateway')}}
|
||||
<li role="none" class="docs-link">
|
||||
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'ingress-gateway')}} rel="noopener noreferrer" target="_blank">
|
||||
Ingress gateways
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</MenuPanel>
|
||||
</dd>
|
||||
</dl>
|
||||
{{else}}
|
||||
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
|
||||
<span>{{Name}}</span>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
{{yield}}
|
||||
{{#if (gt items.length 0)}}
|
||||
<ListCollection @items={{items}} class="consul-service-list" as |item index|>
|
||||
<a data-test-service-name href={{href-to routeName item.Name}} class={{service/health-checks item}}>
|
||||
{{#if (eq item.Kind 'terminating-gateway')}}
|
||||
<a data-test-service-name href={{href-to "dc.services.show.services" item.Name}} class={{service/health-checks item}}>
|
||||
{{item.Name}}
|
||||
</a>
|
||||
{{else if (eq item.Kind 'ingress-gateway')}}
|
||||
<a data-test-service-name href={{href-to "dc.services.show.upstreams" item.Name}} class={{service/health-checks item}}>
|
||||
{{item.Name}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a data-test-service-name href={{href-to "dc.services.show.instances" item.Name}} class={{service/health-checks item}}>
|
||||
{{item.Name}}
|
||||
</a>
|
||||
{{/if}}
|
||||
<ul>
|
||||
{{#if (and nspace (env 'CONSUL_NSPACES_ENABLED'))}}
|
||||
{{#if (not-eq item.Namespace nspace)}}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{#if dc}}
|
||||
<ul>
|
||||
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
|
||||
<li data-test-nspace-menu>
|
||||
<li class="nspaces" data-test-nspace-menu>
|
||||
{{#if (and (eq nspaces.length 1) (not canManageNspaces)) }}
|
||||
<span data-test-nspace-selected={{nspace.Name}}>{{nspace.Name}}</span>
|
||||
{{ else }}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{{yield}}
|
||||
<div class="menu-panel {{position}}">
|
||||
<YieldSlot @name="controls">
|
||||
{{yield}}
|
||||
</YieldSlot>
|
||||
{{#yield-slot name="header"}}
|
||||
<div>{{yield}}</div>
|
||||
{{else}}
|
||||
{{/yield-slot}}
|
||||
<ul role="menu" ...attributes>
|
||||
<YieldSlot @name="menu">
|
||||
{{yield}}
|
||||
</YieldSlot>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
import Slotted from 'block-slots';
|
||||
|
||||
export default Component.extend(Slotted, {
|
||||
tagName: '',
|
||||
});
|
|
@ -11,21 +11,22 @@
|
|||
</YieldSlot>
|
||||
</button>
|
||||
</ToggleButton>
|
||||
<div class={{position}}>
|
||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
|
||||
{{#each submenus as |sub|}}
|
||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
|
||||
{{/each}}
|
||||
{{#yield-slot name='header'}}
|
||||
<div>
|
||||
{{yield}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{/yield-slot}}
|
||||
<ul role="menu" id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
|
||||
<MenuPanel @position={{position}} id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
|
||||
<BlockSlot @name="controls">
|
||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
|
||||
{{#each submenus as |sub|}}
|
||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
|
||||
{{/each}}
|
||||
</BlockSlot>
|
||||
{{#if hasHeader}}
|
||||
<BlockSlot @name="header">
|
||||
{{#yield-slot name="header"}}{{yield}}{{else}}{{/yield-slot}}
|
||||
</BlockSlot>
|
||||
{{/if}}
|
||||
<BlockSlot @name="menu">
|
||||
<YieldSlot @name="menu" @params={{block-params (concat "popover-menu-" guid "-") send keypressClick this.toggle.click}}>
|
||||
{{yield}}
|
||||
</YieldSlot>
|
||||
</ul>
|
||||
</div>
|
||||
</BlockSlot>
|
||||
</MenuPanel>
|
||||
</AriaMenu>
|
|
@ -2,6 +2,7 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Slotted from 'block-slots';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
export default Component.extend(Slotted, {
|
||||
tagName: '',
|
||||
|
@ -16,6 +17,9 @@ export default Component.extend(Slotted, {
|
|||
this._super(...arguments);
|
||||
this.guid = this.dom.guid(this);
|
||||
},
|
||||
willRender: function() {
|
||||
set(this, 'hasHeader', this._isRegistered('header'));
|
||||
},
|
||||
actions: {
|
||||
change: function(e) {
|
||||
if (!e.target.checked) {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<input id={{concat 'sort-control-value-' elementId}} type="radio" value="Name" name={{concat 'sort-control-' name}} onchange={{action 'change'}} />
|
||||
{{#if checked}}
|
||||
<input id={{concat 'sort-control-direction-' elementId}} type="checkbox" onchange={{action 'change'}} />
|
||||
{{/if}}
|
||||
<label for={{if checked (concat 'sort-control-direction-' elementId) (concat 'sort-control-value-' elementId)}}>
|
||||
{{yield}}
|
||||
</label>
|
|
@ -1,15 +0,0 @@
|
|||
import Component from '@ember/component';
|
||||
import { set } from '@ember/object';
|
||||
export default Component.extend({
|
||||
classNames: ['sort-control'],
|
||||
direction: 'asc',
|
||||
onchange: function() {},
|
||||
actions: {
|
||||
change: function(e) {
|
||||
if (e.target.type === 'checkbox') {
|
||||
set(this, 'direction', e.target.checked ? 'desc' : 'asc');
|
||||
}
|
||||
this.onchange({ target: { value: `${this.value}:${this.direction}` } });
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export default helper(function fromEntries(params /*, hash*/) {
|
||||
return Object.fromEntries(params);
|
||||
});
|
|
@ -12,4 +12,5 @@ export default Model.extend({
|
|||
// TODO: Are these required?
|
||||
Services: hasMany('service'),
|
||||
Nodes: hasMany('node'),
|
||||
MeshEnabled: attr('boolean', { defaultValue: true }),
|
||||
});
|
||||
|
|
|
@ -30,23 +30,7 @@ export default Route.extend({
|
|||
.catch(function() {
|
||||
return null;
|
||||
}),
|
||||
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.
|
||||
// The extra code here is mainly for documentation purposes
|
||||
// and for if we need to perform different actions based on the error code
|
||||
// in the future
|
||||
switch (code) {
|
||||
case '500':
|
||||
// connect is likely to be disabled
|
||||
// we just return a null to hide the tab
|
||||
// `Connect must be enabled in order to use this endpoint`
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
chain: this.chainRepo.findBySlug(params.name, dc, nspace),
|
||||
proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace),
|
||||
...model,
|
||||
});
|
||||
|
|
|
@ -39,6 +39,9 @@ export default Service.extend({
|
|||
});
|
||||
}
|
||||
},
|
||||
peekOne: function(id) {
|
||||
return this.store.peekRecord(this.getModelName(), id);
|
||||
},
|
||||
findAllByDatacenter: function(dc, nspace, configuration = {}) {
|
||||
const query = {
|
||||
dc: dc,
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import { get, set } from '@ember/object';
|
||||
import RepositoryService from 'consul-ui/services/repository';
|
||||
|
||||
const modelName = 'discovery-chain';
|
||||
const ERROR_MESH_DISABLED = 'Connect must be enabled in order to use this endpoint';
|
||||
export default RepositoryService.extend({
|
||||
dcs: service('repository/dc'),
|
||||
getModelName: function() {
|
||||
return modelName;
|
||||
},
|
||||
findBySlug: function(slug, dc, nspace, configuration = {}) {
|
||||
const datacenter = this.dcs.peekOne(dc);
|
||||
if (datacenter !== null && !get(datacenter, 'MeshEnabled')) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._super(...arguments).catch(e => {
|
||||
const code = get(e, 'errors.firstObject.status');
|
||||
const body = get(e, 'errors.firstObject.detail').trim();
|
||||
switch (code) {
|
||||
case '500':
|
||||
if (datacenter !== null && body === ERROR_MESH_DISABLED) {
|
||||
set(datacenter, 'MeshEnabled', false);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -36,8 +36,8 @@ const createProxy = function(repo, find, settings, cache, serialize = JSON.strin
|
|||
// original find... with configuration now added
|
||||
return repo[find](...args)
|
||||
.then(res => {
|
||||
if (!configuration.settings.enabled) {
|
||||
// blocking isn't enabled, immediately close
|
||||
if (!configuration.settings.enabled || typeof res === 'undefined') {
|
||||
// blocking isn't enabled, or we got no data, immediately close
|
||||
this.close();
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
@extend %user-select-none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
}
|
||||
%button:disabled,
|
||||
%internal-button:disabled {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
%menu-panel {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
}
|
||||
%menu-panel [type='checkbox'] {
|
||||
display: none;
|
||||
|
@ -15,8 +14,14 @@
|
|||
/* or be hardcoded */
|
||||
/* min-height: 143px; */
|
||||
}
|
||||
%menu-panel [role='menuitem']::after {
|
||||
float: right;
|
||||
%menu-panel [role='menuitem'] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
%menu-panel [role='menuitem']:after {
|
||||
@extend %as-pseudo;
|
||||
display: block !important;
|
||||
background-position: center right !important;
|
||||
}
|
||||
%menu-panel-sub-panel {
|
||||
position: absolute;
|
||||
|
@ -70,7 +75,7 @@
|
|||
}
|
||||
%menu-panel-header {
|
||||
padding: 10px;
|
||||
padding-left: 36px;
|
||||
white-space: normal;
|
||||
}
|
||||
/* here the !important is only needed for what seems to be a difference */
|
||||
/* with the CSS before and after compression */
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
border-top: $decor-border-100;
|
||||
border-color: $gray-300;
|
||||
}
|
||||
%menu-panel-header {
|
||||
background-color: $gray-050;
|
||||
|
||||
%menu-panel .docs-link a::after {
|
||||
@extend %with-docs-mask, %as-pseudo;
|
||||
}
|
||||
%menu-panel-header::before {
|
||||
@extend %with-info-circle-fill-color-icon, %as-pseudo;
|
||||
font-size: 1.1em;
|
||||
%menu-panel .learn-link a::after {
|
||||
@extend %with-learn-mask, %as-pseudo;
|
||||
}
|
||||
%menu-panel .is-active > *::after {
|
||||
@extend %with-check-plain-mask, %as-pseudo;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
padding: 0 8px;
|
||||
border-radius: $decor-radius-100;
|
||||
height: 18px;
|
||||
display: inline-block;
|
||||
line-height: 0.7rem;
|
||||
}
|
||||
%reduced-pill > span {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
%popover-menu + label > * {
|
||||
@extend %toggle-button;
|
||||
}
|
||||
%more-popover-menu-panel,
|
||||
%popover-menu-panel {
|
||||
@extend %menu-panel;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
@extend %toggle-button;
|
||||
}
|
||||
%more-popover-menu-panel {
|
||||
@extend %menu-panel;
|
||||
overflow: hidden;
|
||||
width: 192px;
|
||||
}
|
||||
%more-popover-menu + label + div {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
%app-view-actions {
|
||||
margin-left: auto;
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
@import './list-collection';
|
||||
@import './grid-collection';
|
||||
@import './popover-select';
|
||||
@import './tooltip-panel';
|
||||
@import './menu-panel';
|
||||
|
||||
/**/
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
@extend %with-learn-icon, %as-pseudo;
|
||||
}
|
||||
%main-nav-horizontal .feedback-link a::after {
|
||||
@extend %with-logo-github-monochrome-icon, %as-pseudo;
|
||||
@extend %with-logo-github-monochrome-mask, %as-pseudo;
|
||||
}
|
||||
%main-header-horizontal::before {
|
||||
background-color: var(--swatch-brand-600, $black);
|
||||
|
|
|
@ -10,6 +10,15 @@
|
|||
%secondary-nav {
|
||||
@extend %main-nav-horizontal;
|
||||
}
|
||||
%primary-nav .nspaces .menu-panel > div {
|
||||
background-color: $gray-050;
|
||||
padding-left: 36px;
|
||||
}
|
||||
%primary-nav .nspaces .menu-panel > div::before {
|
||||
@extend %with-info-circle-fill-mask, %as-pseudo;
|
||||
color: $blue-500;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
%main-header-horizontal > div {
|
||||
@extend %main-nav-horizontal-panel;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.menu-panel {
|
||||
@extend %menu-panel;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
@import './tooltip-panel/index';
|
||||
.tooltip-panel {
|
||||
@extend %tooltip-panel;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
|
@ -0,0 +1,21 @@
|
|||
%tooltip-panel {
|
||||
margin: 0 !important;
|
||||
}
|
||||
%tooltip-panel dd > div {
|
||||
top: auto !important;
|
||||
}
|
||||
%tooltip-panel dt {
|
||||
cursor: pointer;
|
||||
}
|
||||
%tooltip-panel dd {
|
||||
display: none;
|
||||
position: relative;
|
||||
padding-top: 10px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
%tooltip-panel:hover dd {
|
||||
display: block;
|
||||
}
|
||||
%tooltip-panel dd > div {
|
||||
width: 250px;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* This is the top chevron */
|
||||
%tooltip-panel dd > div::before {
|
||||
@extend %as-pseudo;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: white;
|
||||
border-top: 1px solid $gray-300;
|
||||
border-right: 1px solid $gray-300;
|
||||
transform: rotate(-45deg);
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: -7px;
|
||||
}
|
|
@ -34,7 +34,7 @@
|
|||
{{#let (sort-by (comparator 'service' sort.selected.key) services) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}} @proxies={{proxies}}/>
|
||||
<ConsulServiceList @items={{filtered}} @proxies={{proxies}}/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{{ item.ID }}
|
||||
</h1>
|
||||
<ConsulExternalSource @item={{item}} />
|
||||
<ConsulKind @item={{item}} />
|
||||
<ConsulKind @item={{item}} @withInfo={{true}} />
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="nav">
|
||||
<dl>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<div class="tab-section">
|
||||
<div role="tabpanel">
|
||||
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
|
||||
<section>
|
||||
<h3>Proxy health checks</h3>
|
||||
<HealthcheckList data-test-proxy-checks @items={{append proxy.ServiceChecks proxy.NodeChecks}} />
|
||||
</section>
|
||||
{{/if}}
|
||||
{{#if (gt proxy.Proxy.Upstreams.length 0)}}
|
||||
<section class="proxy-upstreams">
|
||||
<h3> Upstreams</h3>
|
||||
|
@ -103,5 +97,11 @@
|
|||
</ul>
|
||||
</section>
|
||||
{{/if}}
|
||||
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
|
||||
<section>
|
||||
<h3>Proxy health</h3>
|
||||
<HealthcheckList data-test-proxy-checks @items={{append proxy.ServiceChecks proxy.NodeChecks}} />
|
||||
</section>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
|
@ -14,27 +14,27 @@
|
|||
{{item.Service.Service}}
|
||||
</h1>
|
||||
<ConsulExternalSource @item={{item.Service}} />
|
||||
<ConsulKind @item={{item.Service}} />
|
||||
<ConsulKind @item={{item.Service}} @withInfo={{true}} />
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="nav">
|
||||
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
||||
<TabNav @items={{
|
||||
compact
|
||||
(array
|
||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||
(if (eq item.Service.Kind 'terminating-gateway')
|
||||
(if (eq item.Service.Kind 'terminating-gateway')
|
||||
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
||||
'')
|
||||
(if (eq item.Service.Kind 'ingress-gateway')
|
||||
(if (eq item.Service.Kind 'ingress-gateway')
|
||||
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
||||
'')
|
||||
(if (not item.Service.Kind)
|
||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||
(if (not item.Service.Kind)
|
||||
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
||||
'')
|
||||
(if chain
|
||||
(if chain.Chain
|
||||
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
||||
'')
|
||||
(if (not item.Service.Kind)
|
||||
(if (not item.Service.Kind)
|
||||
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
||||
'')
|
||||
)
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"@glimmer/component": "^1.0.0",
|
||||
"@glimmer/tracking": "^1.0.0",
|
||||
"@hashicorp/consul-api-double": "^2.6.2",
|
||||
"@hashicorp/ember-cli-api-double": "^3.0.2",
|
||||
"@hashicorp/ember-cli-api-double": "^3.1.0",
|
||||
"@xstate/fsm": "^1.4.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"base64-js": "^1.3.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / kvs / trailing-slash
|
||||
Scenario: I have 10 folders
|
||||
Scenario: I have 10 folders and I visit without a trailing slash
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 10 kv models from yaml
|
||||
When I visit the kvs page for yaml
|
||||
|
@ -10,6 +10,9 @@ Feature: dc / kvs / trailing-slash
|
|||
---
|
||||
Then the url should be /datacenter/kv/foo/bar/
|
||||
And a GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F&ns=@namespace"
|
||||
Scenario: I have 10 folders and I visit with a trailing slash
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 10 kv models from yaml
|
||||
When I visit the kvs page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
|
|
|
@ -17,13 +17,20 @@ Feature: dc / services / show-routing: Show Routing for Service
|
|||
And the title should be "service-0 - Consul"
|
||||
And I 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"
|
||||
Given 2 datacenter models from yaml
|
||||
---
|
||||
- dc1
|
||||
- dc2
|
||||
---
|
||||
And 1 node models
|
||||
And 1 service model from yaml
|
||||
And 2 service model from yaml
|
||||
---
|
||||
- Service:
|
||||
Name: service-0
|
||||
ID: service-0-with-id
|
||||
- Service:
|
||||
Name: service-1
|
||||
ID: service-1-with-id
|
||||
---
|
||||
And the url "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace" responds with from yaml
|
||||
---
|
||||
|
@ -37,4 +44,17 @@ Feature: dc / services / show-routing: Show Routing for Service
|
|||
---
|
||||
And I don't see routing on the tabs
|
||||
And I don't see the "[data-test-error]" element
|
||||
|
||||
And I visit the service page for yaml
|
||||
---
|
||||
dc: dc2
|
||||
service: service-1
|
||||
---
|
||||
And I see routing on the tabs
|
||||
And I visit the service page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
service: service-0
|
||||
---
|
||||
Then a GET request wasn't made to "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace"
|
||||
And I don't see routing on the tabs
|
||||
And I don't see the "[data-test-error]" element
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | sort control', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.actions = {};
|
||||
this.send = (actionName, ...args) => this.actions[actionName].apply(this, args);
|
||||
});
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`{{sort-control}}`);
|
||||
|
||||
assert.dom('*').hasText('');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
{{#sort-control}}
|
||||
template block text
|
||||
{{/sort-control}}
|
||||
`);
|
||||
|
||||
assert.dom('*').hasText('template block text');
|
||||
});
|
||||
test('it changes direction and calls onchange when clicked/activated', async function(assert) {
|
||||
assert.expect(2);
|
||||
let count = 0;
|
||||
this.actions.change = e => {
|
||||
if (count === 0) {
|
||||
assert.equal(e.target.value, 'sort:desc');
|
||||
} else {
|
||||
assert.equal(e.target.value, 'sort:asc');
|
||||
}
|
||||
count++;
|
||||
};
|
||||
await render(hbs`{{sort-control checked=true value='sort' onchange=(action 'change')}}`);
|
||||
const $label = this.$('label');
|
||||
$label.trigger('click');
|
||||
$label.trigger('click');
|
||||
});
|
||||
});
|
|
@ -21,9 +21,11 @@ import assertForm from './steps/assertions/form';
|
|||
const pluralize = function(str) {
|
||||
return Inflector.inflector.pluralize(str);
|
||||
};
|
||||
const getLastNthRequest = function(arr) {
|
||||
const getLastNthRequest = function(getRequests) {
|
||||
return function(n, method) {
|
||||
let requests = arr.slice(0).reverse();
|
||||
let requests = getRequests()
|
||||
.slice(0)
|
||||
.reverse();
|
||||
if (method) {
|
||||
requests = requests.filter(function(item) {
|
||||
return item.method === method;
|
||||
|
@ -82,7 +84,7 @@ export default function(assert, library) {
|
|||
})();
|
||||
});
|
||||
};
|
||||
const lastNthRequest = getLastNthRequest(api.server.history);
|
||||
const lastNthRequest = getLastNthRequest(() => api.server.history);
|
||||
const create = function(number, name, value) {
|
||||
// don't return a promise here as
|
||||
// I don't need it to wait
|
||||
|
@ -99,6 +101,7 @@ export default function(assert, library) {
|
|||
return currentPage;
|
||||
};
|
||||
const setCurrentPage = function(page) {
|
||||
api.server.clearHistory();
|
||||
currentPage = page;
|
||||
return page;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const not = `(n't| not)?`;
|
||||
export default function(scenario, assert, lastNthRequest) {
|
||||
// lastNthRequest should return a
|
||||
// {
|
||||
|
@ -21,12 +22,17 @@ export default function(scenario, assert, lastNthRequest) {
|
|||
);
|
||||
assert.equal(diff.size, 0, `Expected requests "${[...diff].join(', ')}"`);
|
||||
})
|
||||
.then('a $method request was made to "$endpoint"', function(method, url) {
|
||||
.then(`a $method request was${not} made to "$endpoint"`, function(method, negative, url) {
|
||||
const isNegative = typeof negative !== 'undefined';
|
||||
const requests = lastNthRequest(null, method);
|
||||
const request = requests.find(function(item) {
|
||||
const request = requests.some(function(item) {
|
||||
return method === item.method && url === item.url;
|
||||
});
|
||||
assert.ok(request, `Expected a ${method} request url to ${url}`);
|
||||
if (isNegative) {
|
||||
assert.notOk(request, `Didn't expect a ${method} request url to ${url}`);
|
||||
} else {
|
||||
assert.ok(request, `Expected a ${method} request url to ${url}`);
|
||||
}
|
||||
})
|
||||
.then('a $method request was made to "$endpoint" with no body', function(method, url) {
|
||||
const requests = lastNthRequest(null, method);
|
||||
|
|
|
@ -1215,10 +1215,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.2.tgz#e2c34a348b9959fcc95ffad797c1fed9644a41bd"
|
||||
integrity sha512-VNdwsL3ut4SubCtwWfqX4prD9R/RczKtWUID6s6K9h1TCdzTgpZQhbb+gdzaYGqzCE3Mrw416JzclxVTIFIUFw==
|
||||
|
||||
"@hashicorp/ember-cli-api-double@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.0.2.tgz#4f66f22e4b54293c46fe16dc24568267526c8acd"
|
||||
integrity sha512-NmcA+jBcBO8tzCfbqWzrQdHvTUeaj71Gdu9phxaULMtM9Zw7BZtHlvb2P1ivknGGw92w9Se50pDNjkB57ww22A==
|
||||
"@hashicorp/ember-cli-api-double@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.1.0.tgz#ce228ac5c8a46c7a10112f5bc0fb782c47775b60"
|
||||
integrity sha512-G8dDSewInFZOeD5sprdZPw7ZKUYlkJ9bJxPkEaMRPbC6ZN4ZHqeFWB1xXeq2ROtR07J6Xbs+BrFIE6GHTshpEg==
|
||||
dependencies:
|
||||
"@hashicorp/api-double" "^1.6.1"
|
||||
array-range "^1.0.1"
|
||||
|
|
Loading…
Reference in New Issue