mirror of https://github.com/status-im/consul.git
ui: Topology Intentions Popovers (#9137)
* Refactor grid styling for Topology page * Crate TopologyMetrics Button component and move styling * Create intention ID * fixup button styling * Return a link to the create intention page * Rename Button to Popover component * Fixup serializer test * ui: Inline Topology Intention Actions (#9153) * Add arrow and dot to/from metrics back in * Add addional space to have metrics wrap and show in smaller screens * Move logic for finding positioning * Use color variables Co-authored-by: John Cowen <johncowen@users.noreply.github.com>
This commit is contained in:
parent
b9f1b19bb8
commit
7243f1f4f9
|
@ -1,11 +1,10 @@
|
|||
{{#each @items as |item|}}
|
||||
<a class="card"
|
||||
href={{href-to "dc.services.show.index" item.Name}}
|
||||
data-permission={{service/intention-permissions item}}
|
||||
id="{{item.Namespace}}{{item.Name}}"
|
||||
href={{href-to "dc.services.show.index" @item.Name}}
|
||||
data-permission={{service/intention-permissions @item}}
|
||||
id="{{@item.Namespace}}{{@item.Name}}"
|
||||
>
|
||||
<p>
|
||||
{{item.Name}}
|
||||
{{@item.Name}}
|
||||
</p>
|
||||
<div class="details">
|
||||
{{#if (and (and nspace (env 'CONSUL_NSPACES_ENABLED')) @type)}}
|
||||
|
@ -16,12 +15,12 @@
|
|||
</Tooltip>
|
||||
</dt>
|
||||
<dd>
|
||||
{{item.Namespace}}
|
||||
{{@item.Namespace}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{/if}}
|
||||
{{#if (eq item.Datacenter @dc)}}
|
||||
{{#let (service/health-percentage item) as |percentage|}}
|
||||
{{#if (eq @item.Datacenter @dc)}}
|
||||
{{#let (service/health-percentage @item) as |percentage|}}
|
||||
{{#if (not-eq percentage.passing 0)}}
|
||||
<span class="passing">{{percentage.passing}}%</span>
|
||||
{{/if}}
|
||||
|
@ -50,17 +49,16 @@
|
|||
<TopologyMetrics::Stats
|
||||
@endpoint='upstream-summary-for-service'
|
||||
@service={{@service.Service}}
|
||||
@item={{item.Name}}
|
||||
@item={{@item.Name}}
|
||||
@noMetricsReason={{@noMetricsReason}}
|
||||
/>
|
||||
{{else}}
|
||||
<TopologyMetrics::Stats
|
||||
@endpoint='downstream-summary-for-service'
|
||||
@service={{@service.Service}}
|
||||
@item={{item.Name}}
|
||||
@item={{@item.Name}}
|
||||
@noMetricsReason={{@noMetricsReason}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</a>
|
||||
{{/each}}
|
||||
</a>
|
|
@ -55,8 +55,11 @@
|
|||
</svg>
|
||||
{{/if}}
|
||||
|
||||
<TopologyMetrics::Icon
|
||||
@positions={{this.iconPositions}}
|
||||
@items={{@items}}
|
||||
/>
|
||||
{{#each @items as |item|}}
|
||||
<TopologyMetrics::Popover
|
||||
@position={{find-by 'id' (concat item.Namespace item.Name) this.iconPositions}}
|
||||
@item={{item}}
|
||||
@oncreate={{action @oncreate item @service}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
{{#each @items as |item|}}
|
||||
{{#let (find-by 'id' (concat item.Namespace item.Name) @positions) as |style|}}
|
||||
{{#if (and (not item.Intention.Allowed) (not item.Intention.HasPermissions))}}
|
||||
<span class="deny" style={{{ concat 'top:' style.y 'px;left:' style.x 'px;'}}}>
|
||||
<Tooltip>
|
||||
An intention is set to 'deny' that prohibits these services from connecting.
|
||||
</Tooltip>
|
||||
</span>
|
||||
{{else if item.Intention.HasPermissions}}
|
||||
<span class="L7" style={{{ concat 'top:' style.y 'px;left:' style.x 'px;'}}}>
|
||||
<Tooltip>
|
||||
The intention between these services has Layer 7 permissions, so certain requests may or may not be permitted.
|
||||
</Tooltip>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{/each}}
|
|
@ -1,6 +1,10 @@
|
|||
{{on-window 'resize' (action this.calculate)}}
|
||||
|
||||
<div {{did-insert (action this.calculate)}} {{did-update (action this.calculate) @topology}} class="topology-container">
|
||||
<div
|
||||
{{did-insert (action this.calculate)}}
|
||||
{{did-update (action this.calculate) @topology.Upstreams @topology.Downstreams}}
|
||||
class="topology-container"
|
||||
>
|
||||
{{#if (gt @topology.Downstreams.length 0)}}
|
||||
<div id="downstream-container">
|
||||
<div>
|
||||
|
@ -11,13 +15,15 @@
|
|||
</Tooltip>
|
||||
</span>
|
||||
</div>
|
||||
{{#each @topology.Downstreams as |item|}}
|
||||
<TopologyMetrics::Card
|
||||
@items={{@topology.Downstreams}}
|
||||
@item={{item}}
|
||||
@service={{@service.Service}}
|
||||
@dc={{@topology.Datacenter}}
|
||||
@hasMetricsProvider={{this.hasMetricsProvider}}
|
||||
@noMetricsReason={{this.noMetricsReason}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div id="metrics-container">
|
||||
|
@ -52,25 +58,29 @@
|
|||
<div id="downstream-lines">
|
||||
<TopologyMetrics::DownLines
|
||||
@type='downstream'
|
||||
@service={{@service}}
|
||||
@view={{this.downView}}
|
||||
@center={{this.centerDimensions}}
|
||||
@lines={{this.downLines}}
|
||||
@items={{@topology.Downstreams}}
|
||||
/>
|
||||
@oncreate={{action @oncreate}}
|
||||
/>
|
||||
</div>
|
||||
{{#if (gt @topology.Upstreams.length 0)}}
|
||||
<div id="upstream-column">
|
||||
{{#each-in (group-by "Datacenter" @topology.Upstreams) as |dc upstreams|}}
|
||||
<div id="upstream-container">
|
||||
<p>{{dc}}</p>
|
||||
{{#each upstreams as |item|}}
|
||||
<TopologyMetrics::Card
|
||||
@items={{upstreams}}
|
||||
@item={{item}}
|
||||
@service={{@service.Service}}
|
||||
@dc={{@topology.Datacenter}}
|
||||
@type='upstream'
|
||||
@hasMetricsProvider={{this.hasMetricsProvider}}
|
||||
@noMetricsReason={{this.noMetricsReason}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each-in}}
|
||||
</div>
|
||||
|
@ -78,10 +88,12 @@
|
|||
<div id="upstream-lines">
|
||||
<TopologyMetrics::UpLines
|
||||
@type='upstream'
|
||||
@service={{@service}}
|
||||
@view={{this.upView}}
|
||||
@center={{this.centerDimensions}}
|
||||
@lines={{this.upLines}}
|
||||
@items={{@topology.Upstreams}}
|
||||
/>
|
||||
@oncreate={{action @oncreate}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -38,7 +38,7 @@ export default class TopologyMetrics extends Component {
|
|||
drawDownLines(items) {
|
||||
const order = ['allow', 'deny'];
|
||||
const dest = {
|
||||
x: this.centerDimensions.x,
|
||||
x: this.centerDimensions.x - 7,
|
||||
y: this.centerDimensions.y + this.centerDimensions.height / 2,
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,7 @@ export default class TopologyMetrics extends Component {
|
|||
drawUpLines(items) {
|
||||
const order = ['allow', 'deny'];
|
||||
const src = {
|
||||
x: this.centerDimensions.x + 20,
|
||||
x: this.centerDimensions.x + 5.5,
|
||||
y: this.centerDimensions.y + this.centerDimensions.height / 2,
|
||||
};
|
||||
|
||||
|
@ -73,7 +73,7 @@ export default class TopologyMetrics extends Component {
|
|||
.map(item => {
|
||||
const dimensions = item.getBoundingClientRect();
|
||||
const dest = {
|
||||
x: dimensions.x - dimensions.width - 26,
|
||||
x: dimensions.x - dimensions.width - 25,
|
||||
y: dimensions.y + dimensions.height / 2,
|
||||
};
|
||||
|
||||
|
|
|
@ -2,35 +2,40 @@
|
|||
display: grid;
|
||||
height: 100%;
|
||||
align-items: start;
|
||||
grid-template-columns: 2fr 20px 1fr 20px 2fr 20px 1fr 20px 2fr;
|
||||
grid-template-columns: 2fr 1fr 2fr 1fr 2fr;
|
||||
grid-template-rows: 50px 1fr 50px;
|
||||
grid-template-areas:
|
||||
"down-cards down-lines . up-lines up-cards"
|
||||
"down-cards down-lines metrics up-lines up-cards"
|
||||
"down-cards down-lines . up-lines up-cards";
|
||||
}
|
||||
|
||||
// Grid Layout
|
||||
#downstream-container {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 1 / 3;
|
||||
grid-area: down-cards;
|
||||
}
|
||||
#downstream-lines {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 2 / 5;
|
||||
grid-area: down-lines;
|
||||
}
|
||||
#upstream-lines {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 6 / 9;
|
||||
grid-area: up-lines;
|
||||
}
|
||||
#upstream-column {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 8 / 10;
|
||||
grid-area: up-cards;
|
||||
}
|
||||
|
||||
// Columns/Containers & Lines
|
||||
#downstream-lines,
|
||||
#upstream-lines {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
#downstream-lines {
|
||||
margin-left: -20px;
|
||||
}
|
||||
#upstream-lines {
|
||||
margin-right: -20px;
|
||||
}
|
||||
#downstream-container,
|
||||
#upstream-container {
|
||||
padding: 12px;
|
||||
|
@ -86,8 +91,7 @@
|
|||
|
||||
// Metrics Container
|
||||
#metrics-container {
|
||||
grid-row: 2 / 3;
|
||||
grid-column: 4 / 7;
|
||||
grid-area: metrics;
|
||||
|
||||
div:first-child {
|
||||
padding: 12px;
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<div class="topology-metrics-popover">
|
||||
{{#if (and (not @item.Intention.Allowed) (not @item.Intention.HasPermissions))}}
|
||||
<button
|
||||
{{on "click" this.togglePopover}}
|
||||
type="button"
|
||||
class="deny-target"
|
||||
style={{{ concat 'top:' @position.y 'px;left:' @position.x 'px;'}}}
|
||||
>
|
||||
<EmberPopover
|
||||
@isShown={{this.showToggleablePopover}}
|
||||
@event="none"
|
||||
@hideOn="mouseleave"
|
||||
@side="bottom-start"
|
||||
@tooltipClass="deny-popover"
|
||||
>
|
||||
<div class="body">
|
||||
<h3>Connection Denied</h3>
|
||||
<p>Add an intention that allows these two services to connect.</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
{{#if @item.Intention.HasExact}}
|
||||
<a href={{href-to 'dc.services.show.intentions.edit' (concat @item.Intention.ID)}}>Edit</a>
|
||||
{{else}}
|
||||
<button
|
||||
{{on "click" @oncreate}}
|
||||
type="button"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
{{/if}}
|
||||
<button
|
||||
{{on "click" this.togglePopover}}
|
||||
class="cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</EmberPopover>
|
||||
</button>
|
||||
{{else if @item.Intention.HasPermissions}}
|
||||
<button
|
||||
{{on "click" this.togglePopover}}
|
||||
type="button"
|
||||
class="L7-target"
|
||||
style={{{ concat 'top:' @position.y 'px;left:' @position.x 'px;'}}}
|
||||
>
|
||||
<EmberPopover
|
||||
@isShown={{this.showToggleablePopover}}
|
||||
@event="none"
|
||||
@hideOn="mouseleave"
|
||||
@side="bottom-start"
|
||||
@tooltipClass="L7-popover"
|
||||
>
|
||||
<div class="body">
|
||||
<h3>Layer 7 permissions</h3>
|
||||
<p>Certain HTTP request info must be identified.</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href={{href-to 'dc.services.show.intentions.edit' (concat @item.Intention.ID)}}>View</a>
|
||||
<button
|
||||
{{on "click" this.togglePopover}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</EmberPopover>
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export default class TopoloyMetricsButton extends Component {
|
||||
// =attributes
|
||||
@tracked showToggleablePopover = false;
|
||||
|
||||
// =actions
|
||||
@action
|
||||
togglePopover() {
|
||||
this.showToggleablePopover = !this.showToggleablePopover;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
.topology-metrics-popover {
|
||||
.deny-target,
|
||||
.L7-target {
|
||||
transform: translate(-50%,-50%);
|
||||
position: absolute;
|
||||
background-color: $white;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
.deny-target:hover,
|
||||
.L7-target:hover {
|
||||
cursor:pointer;
|
||||
}
|
||||
.deny-target:active,
|
||||
.deny-target:focus,
|
||||
.L7-target:active,
|
||||
.L7-target:focus {
|
||||
outline: none;
|
||||
}
|
||||
.deny-target::before {
|
||||
@extend %with-cancel-square-fill-color-mask, %as-pseudo;
|
||||
background-color: $red-500;
|
||||
}
|
||||
.L7-target::before {
|
||||
@extend %with-layers-mask, %as-pseudo;
|
||||
background-color: $gray-300;
|
||||
}
|
||||
.body {
|
||||
padding: 12px 12px 16px 25px;
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
p {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
border-top: 1px solid $gray-300;
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
a,
|
||||
button {
|
||||
width: 50%;
|
||||
height: 36px;
|
||||
padding: 10px 0;
|
||||
font-weight: $typo-weight-medium;
|
||||
text-align: center;
|
||||
line-height: normal;
|
||||
}
|
||||
button:first-child {
|
||||
color: $blue-500;
|
||||
}
|
||||
button:nth-child(2) {
|
||||
color: $gray-800;
|
||||
}
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.ember-popover {
|
||||
padding: 0;
|
||||
width: 200px;
|
||||
z-index: 3 !important;
|
||||
}
|
||||
}
|
||||
.L7-popover {
|
||||
.body {
|
||||
background-color: $white;
|
||||
}
|
||||
h3 {
|
||||
margin-left: -20px;
|
||||
color: $blue-500;
|
||||
}
|
||||
h3::before {
|
||||
@extend %with-info-circle-fill-mask, %as-pseudo;
|
||||
color: $blue-500;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
.deny-popover {
|
||||
.body {
|
||||
background-color: $white;
|
||||
}
|
||||
h3 {
|
||||
color: $red-800;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
padding: 0;
|
||||
margin: 0;
|
||||
height: 70px;
|
||||
z-index: 2;
|
||||
|
||||
svg.sparkline {
|
||||
width: 100%;
|
||||
|
@ -16,7 +15,7 @@
|
|||
.tooltip {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
z-index: 10;
|
||||
bottom: 78px;
|
||||
width: 217px;
|
||||
}
|
||||
|
|
|
@ -123,22 +123,3 @@
|
|||
stroke-linejoin: round;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Icon on SVG Lines
|
||||
#downstream-lines,
|
||||
#upstream-lines {
|
||||
.deny::before {
|
||||
@extend %with-cancel-square-fill-color-mask, %as-pseudo;
|
||||
background-color: $red-500;
|
||||
}
|
||||
.L7::before {
|
||||
@extend %with-layers-mask, %as-pseudo;
|
||||
background-color: $gray-300;
|
||||
}
|
||||
span {
|
||||
transform: translate(-50%,-50%);
|
||||
position: absolute;
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
.stats {
|
||||
padding: 12px;
|
||||
padding: 12px 12px 0 12px;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
overflow: hidden;
|
||||
dl {
|
||||
display:flex;
|
||||
margin-bottom: 50px; // pushes wrapped metrics well out of the bounding box to hide them.
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
dt {
|
||||
margin-right: 5px;
|
||||
|
|
|
@ -55,7 +55,10 @@
|
|||
</svg>
|
||||
{{/if}}
|
||||
|
||||
<TopologyMetrics::Icon
|
||||
@positions={{this.iconPositions}}
|
||||
@items={{@items}}
|
||||
/>
|
||||
{{#each @items as |item|}}
|
||||
<TopologyMetrics::Popover
|
||||
@position={{find-by 'id' (concat item.Namespace item.Name) this.iconPositions}}
|
||||
@item={{item}}
|
||||
@oncreate={{action @oncreate @service item}}
|
||||
/>
|
||||
{{/each}}
|
||||
|
|
|
@ -2,7 +2,7 @@ import Component from '@glimmer/component';
|
|||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class TopoloyMetricsUpLines extends Component {
|
||||
export default class TopologyMetricsUpLines extends Component {
|
||||
@tracked iconPositions;
|
||||
|
||||
@action
|
||||
|
|
|
@ -2,13 +2,27 @@ import { inject as service } from '@ember/service';
|
|||
import Route from 'consul-ui/routing/route';
|
||||
import { hash } from 'rsvp';
|
||||
import { get } from '@ember/object';
|
||||
import { action, setProperties } from '@ember/object';
|
||||
|
||||
export default class ShowRoute extends Route {
|
||||
@service('data-source/service')
|
||||
data;
|
||||
@service('data-source/service') data;
|
||||
@service('repository/intention') repo;
|
||||
@service('ui-config') config;
|
||||
|
||||
@service('ui-config')
|
||||
config;
|
||||
@action
|
||||
async createIntention(source, destination) {
|
||||
const intention = service.Intention;
|
||||
const model = this.repo.create({
|
||||
Datacenter: source.Datacenter,
|
||||
SourceName: source.Name,
|
||||
SourceNS: source.Namespace || 'default',
|
||||
DestinationName: destination.Name,
|
||||
DestinationNS: destination.Namespace || 'default',
|
||||
Action: 'allow',
|
||||
});
|
||||
await this.repo.persist(model);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
model(params, transition) {
|
||||
const dc = this.modelFor('dc').dc.Name;
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
import Serializer from './application';
|
||||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/topology';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class TopologySerializer extends Serializer {
|
||||
@service('store') store;
|
||||
|
||||
primaryKey = PRIMARY_KEY;
|
||||
slugKey = SLUG_KEY;
|
||||
|
||||
respondForQueryRecord(respond, query) {
|
||||
const intentionSerializer = this.store.serializerFor('intention');
|
||||
return super.respondForQueryRecord(function(cb) {
|
||||
return respond(function(headers, body) {
|
||||
body.Downstreams.forEach(item => {
|
||||
item.Intention.SourceName = item.Name;
|
||||
item.Intention.SourceNS = item.Namespace;
|
||||
item.Intention.DestinationName = query.id;
|
||||
item.Intention.DestinationNS = query.ns || 'default';
|
||||
intentionSerializer.ensureID(item.Intention);
|
||||
});
|
||||
body.Upstreams.forEach(item => {
|
||||
item.Intention.SourceName = query.id;
|
||||
item.Intention.SourceNS = query.ns || 'default';
|
||||
item.Intention.DestinationName = item.Name;
|
||||
item.Intention.DestinationNS = item.Namespace;
|
||||
intentionSerializer.ensureID(item.Intention);
|
||||
});
|
||||
return cb(headers, {
|
||||
...body,
|
||||
[SLUG_KEY]: query.id,
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
@import 'consul-ui/components/consul/intention';
|
||||
@import 'consul-ui/components/role-selector';
|
||||
@import 'consul-ui/components/topology-metrics';
|
||||
@import 'consul-ui/components/topology-metrics/popover';
|
||||
@import 'consul-ui/components/topology-metrics/series';
|
||||
@import 'consul-ui/components/topology-metrics/stats';
|
||||
@import 'consul-ui/components/topology-metrics/status';
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
Datacenter=dc
|
||||
Service=items.firstObject
|
||||
)}}
|
||||
|
||||
@oncreate={{route-action 'createIntention'}}
|
||||
/>
|
||||
{{else}}
|
||||
<EmptyState>
|
||||
|
|
|
@ -123,6 +123,7 @@
|
|||
"ember-ref-modifier": "^1.0.0",
|
||||
"ember-render-helpers": "^0.1.1",
|
||||
"ember-resolver": "^8.0.0",
|
||||
"ember-route-action-helper": "^2.0.8",
|
||||
"ember-router-helpers": "^0.4.0",
|
||||
"ember-sinon-qunit": "5.0.0",
|
||||
"ember-source": "~3.20.5",
|
||||
|
|
|
@ -10,8 +10,9 @@ module('Integration | Serializer | topology', function(hooks) {
|
|||
const serializer = this.owner.lookup('serializer:topology');
|
||||
const dc = 'dc-1';
|
||||
const id = 'slug';
|
||||
const kind = '';
|
||||
const request = {
|
||||
url: `/v1/internal/ui/service-topology/${id}?dc=${dc}`,
|
||||
url: `/v1/internal/ui/service-topology/${id}?dc=${dc}&kind=${kind}`,
|
||||
};
|
||||
return get(request.url).then(function(payload) {
|
||||
const expected = {
|
||||
|
@ -28,6 +29,7 @@ module('Integration | Serializer | topology', function(hooks) {
|
|||
{
|
||||
dc: dc,
|
||||
id: id,
|
||||
kind: kind,
|
||||
}
|
||||
);
|
||||
assert.equal(actual.Datacenter, expected.Datacenter);
|
||||
|
|
23
ui/yarn.lock
23
ui/yarn.lock
|
@ -8031,6 +8031,13 @@ ember-export-application-global@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/ember-export-application-global/-/ember-export-application-global-2.0.1.tgz#b120a70e322ab208defc9e2daebe8d0dfc2dcd46"
|
||||
integrity sha512-B7wiurPgsxsSGzJuPFkpBWnaeuCu2PGpG2BjyrfA1VcL7//o+5RSnZqiCEY326y7qmxb2GoCgo0ft03KBU0rRw==
|
||||
|
||||
ember-factory-for-polyfill@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/ember-factory-for-polyfill/-/ember-factory-for-polyfill-1.3.1.tgz#b446ed64916d293c847a4955240eb2c993b86eae"
|
||||
integrity sha512-y3iG2iCzH96lZMTWQw6LWNLAfOmDC4pXKbZP6FxG8lt7GGaNFkZjwsf+Z5GAe7kxfD7UG4lVkF7x37K82rySGA==
|
||||
dependencies:
|
||||
ember-cli-version-checker "^2.1.0"
|
||||
|
||||
ember-get-config@^0.2.4:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/ember-get-config/-/ember-get-config-0.2.4.tgz#118492a2a03d73e46004ed777928942021fe1ecd"
|
||||
|
@ -8039,6 +8046,14 @@ ember-get-config@^0.2.4:
|
|||
broccoli-file-creator "^1.1.1"
|
||||
ember-cli-babel "^6.3.0"
|
||||
|
||||
ember-getowner-polyfill@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-getowner-polyfill/-/ember-getowner-polyfill-2.2.0.tgz#38e7dccbcac69d5ec694000329ec0b2be651d2b2"
|
||||
integrity sha512-rwGMJgbGzxIAiWYjdpAh04Abvt0s3HuS/VjHzUFhVyVg2pzAuz45B9AzOxYXzkp88vFC7FPaiA4kE8NxNk4A4Q==
|
||||
dependencies:
|
||||
ember-cli-version-checker "^2.1.0"
|
||||
ember-factory-for-polyfill "^1.3.1"
|
||||
|
||||
ember-href-to@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-href-to/-/ember-href-to-3.1.0.tgz#704f66c2b555a2685fac9ddc74eb9c95abaf5b8f"
|
||||
|
@ -8222,6 +8237,14 @@ ember-rfc176-data@^0.3.12, ember-rfc176-data@^0.3.13, ember-rfc176-data@^0.3.16:
|
|||
resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.16.tgz#2ace0ac9cf9016d493a74a1d931643a308679803"
|
||||
integrity sha512-IYAzffS90r2ybAcx8c2qprYfkxa70G+/UPkxMN1hw55DU5S2aLOX6v3umKDZItoRhrvZMCnzwsdfKSrKdC9Wbg==
|
||||
|
||||
ember-route-action-helper@^2.0.8:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/ember-route-action-helper/-/ember-route-action-helper-2.0.8.tgz#f227fcccb73e839b65e9b814e241b322fe8c02fc"
|
||||
integrity sha512-V+4uKwqaYveriVt2rl4e+9mzHJiQOr1B8dCPQQ2TS3iAcmi5RD2giRDFGtCK9d2XY9Arb/f9hJh0obP20iyt3A==
|
||||
dependencies:
|
||||
ember-cli-babel "^6.8.1"
|
||||
ember-getowner-polyfill "^2.0.0"
|
||||
|
||||
ember-router-generator@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-router-generator/-/ember-router-generator-2.0.0.tgz#d04abfed4ba8b42d166477bbce47fccc672dbde0"
|
||||
|
|
Loading…
Reference in New Issue