Epic: Evaluation Detail Sidebar (#12370)

* chore: prettify gutter-menu

* chore:  add portal packages

* styling:  add styles sidebar and portal behavior

* ui:  sidebar component

* ui:  create and implement statechart for evals

* ui:  actor-relationship service and provider component

* ui:  d3 hierarchy computation

* chore:  add render-modifiers and curved arrows

* ui:  create evaluation actor div

* fix related evaluations schema

* ui:  register/deregister evaluation divs

* ui:  handle resize behavior

* bug:  infinite re-render cycle

* fix:  conditional logic to prevent infinite render of flex resizing

* ui: related evaluations schema and request param

* ui: fix testing for evaluations

* refact: make related-evals a proper has-many

* chore: don't pauseTest

* temp:  debug d3 hierarchy

* ui:  move derived state logic into backing component class for detail

* ui:  deprecated related evaluations logic in statechart

* ui:  update evaluation models

* ui:  update logic to paint svg in non-viewable scroll region

* ui:  update styling

* ui:  testing for eval detail view

* ui:  delete detail from template directory

* ui:  break detail component down

* ui:  static data for /evaluation/:id endpoint

* ui:  fix styling of d3 viz

* ui:  add query parameter adapter for evals

* ui:  last minute design requests

* wip:  address browser updating detail view behavior

* refact: handle query-state change in statechart

* conditional class looking for currentEval equality (#12411)

* F UI/evaluation detail sidebar rel evals (#12415)

* ui:  remove busy id alias from statechart

* ui: edit related evaluations viz error message

* ui:  bug fixes on related evaluations view (#12423)

* ui:  remove busy id alias from statechart

* ui: edit related evaluations viz error message

* ui:  update error state

* ui:  related evaluation outline styling

* Related evaluation stylefile and non-link if it matches the active sidebar (#12428)

* Adds tabbable and keyboard pressable evaluation table rows (#12433)

* ui:  fix failing eval list tests (#12437)

* ui:  move styling into classes (#12438)

* fix test failures (#12444)

* ui:  move styling into classes

* ui:  eslint disable

* ui:  allocations have evaluations as async relationships

* ui:  fix evaluation refresh button (#12447)

* ui:  move styling into classes

* ui:  eslint disable

* ui:  allocations have evaluations as async relationships

* ui:  refresh bug

* ui:  final touches on sidebar (#12462)

* chore: turn off template linting rules

Temporarily turning off template linting because we dont have a set CSS convention and the release needs to go out ASAP.

* doc:  deprecate out of date comments and vars

* ui:  edit mirage server fetch logic

* ui:  style sidebar relative

* Modification to mocked related evals and manually set 100% height on svg (#12460)

* F UI/evaluation detail sidebar final touches (#12463)

* chore: turn off template linting rules

Temporarily turning off template linting because we dont have a set CSS convention and the release needs to go out ASAP.

* doc:  deprecate out of date comments and vars

* ui:  edit mirage server fetch logic

* ui:  style sidebar relative

* ui:  account for new related eval added to chain

Co-authored-by: Michael Klein <michael@firstiwaslike.com>
Co-authored-by: Phil Renaud <phil@riotindustries.com>
This commit is contained in:
Jai
2022-04-05 14:34:37 -04:00
committed by GitHub
parent d412f7b497
commit 3e0a1e19ad
26 changed files with 2048 additions and 170 deletions

View File

@@ -3,7 +3,9 @@
module.exports = {
extends: 'recommended',
rules: {
'link-href-attributes': 'off',
'no-action': 'off',
'no-invalid-interactive': 'off',
'no-inline-styles': 'off',
},
};

View File

@@ -8,4 +8,13 @@ export default class EvaluationAdapter extends ApplicationAdapter {
result.meta = { nextToken: headers['x-nomad-nexttoken'] };
return result;
}
urlForFindRecord(_id, _modelName, snapshot) {
const url = super.urlForFindRecord(...arguments);
if (snapshot.adapterOptions?.related) {
return `${url}?related=true`;
}
return url;
}
}

View File

@@ -0,0 +1,168 @@
{{#let this.currentEvalDetail as |evaluation|}}
<Portal @target="eval-detail-portal">
<div
data-test-eval-detail
data-test-eval-detail-is-open={{this.isSideBarOpen}}
class="sidebar {{if this.isSideBarOpen "open"}}"
{{on-click-outside
this.closeSidebar
capture=true
exceptSelector="tr[data-eval-row]"
}}
>
{{#if this.isLoading}}
<div data-test-eval-loading>
<section class="section has-text-centered">
<LoadingSpinner />
</section>
</div>
{{/if}}
{{#if this.isError}}
<div data-test-eval-detail-header class="error-header">
<button
data-test-eval-sidebar-x
class="button is-borderless"
type="button"
{{on "click" this.closeSidebar}}
>
{{x-icon "cancel"}}
</button>
</div>
<div class="error-container">
<div data-test-eval-error class="error-message">
<h1 data-test-error-title class="title is-spaced">
Not Found
</h1>
<p data-test-error-message class="subtitle">
The requested evaluation could not be found. You may not be authorized to view this evaluation, it may have been garbage collected, or the ID is invalid.
</p>
</div>
</div>
{{/if}}
{{#if this.isSuccess}}
{{! Evaluation Detail Header}}
<div data-test-eval-detail-header class="detail-header">
<h1 data-test-title class="title">
{{evaluation.shortId}}
<span class="bumper-left tag is-primary">
{{evaluation.status}}
</span>
</h1>
<button
data-test-eval-sidebar-x
class="button is-borderless"
type="button"
{{on "click" this.closeSidebar}}
>
{{x-icon "cancel"}}
</button>
</div>
{{! Start Evaluation Stats}}
<div class="boxed-section is-small">
<div
class="boxed-section-body inline-definitions"
style="display: flex;"
>
<span class="label" style="width: 6.125rem;">
Evaluation Details
</span>
<div style="display: flex; flex-direction: column">
<span class="pair">
<span class="term">
Job
</span>
<LinkTo
data-test-evaluation-job
@model={{concat evaluation.plainJobId "@" evaluation.namespace
}}
@route="jobs.job"
>
{{evaluation.plainJobId}}
</LinkTo>
</span>
<span class="pair">
<span class="term">
Triggered By
</span>
{{evaluation.triggeredBy}}
</span>
</div>
<div style="display: flex; flex-direction: column">
<span class="pair">
<span class="term">
Priority
</span>
{{evaluation.priority}}
</span>
</div>
<div style="display: flex; flex-direction: column">
<span class="pair">
<span class="term">
Created
</span>
{{format-month-ts evaluation.createTime}}
</span>
<span class="pair">
<span class="term">
Placement Failure
</span>
{{evaluation.hasPlacementFailures}}
</span>
</div>
</div>
</div>
{{! Placement Failures}}
{{#if evaluation.failedTGAllocs}}
<div class="boxed-section is-danger">
<div class="boxed-section-head">
Placement Failures
</div>
<div class="boxed-section-body">
{{#each evaluation.failedTGAllocs as |placementFailure|}}
<PlacementFailure @failedTGAlloc={{placementFailure}} />
{{/each}}
</div>
</div>
{{/if}}
{{! Related Evaluations}}
{{#if this.descendentsMap}}
<EvaluationSidebar::RelatedEvaluations
@fns={{hash
handleResize=this.handleResize
handleEvaluationClick=@fns.handleEvaluationClick
}}
@data={{hash
width=this.width
height=this.height
parentEvaluation=this.parentEvaluation
descendentsMap=this.descendentsMap
activeEvaluationID=this.currentEvalDetail.id
}}
/>
{{else}}
<div class="boxed-section">
<div class="boxed-section-head">
Related Evaluations
</div>
<div class="boxed-section-body">
<div data-test-eval-error class="error-message title">
<p data-test-error-message class="subtitle">
The related evaluations cannot be visualized. Parts of the related evaluation tree may have been garbage collected.
</p>
</div>
</div>
</div>
{{/if}}
{{! Evaluation JSON Response}}
<div class="boxed-section">
<div class="boxed-section-head">
Evaluation Response
</div>
<div class="boxed-section-body is-full-bleed">
<JsonViewer @json={{evaluation}} />
</div>
</div>
{{/if}}
</div>
</Portal>
{{/let}}

View File

@@ -0,0 +1,78 @@
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import d3 from 'd3';
import { matchesState } from 'ember-statecharts';
export default class Detail extends Component {
get statechart() {
return this.args.statechart;
}
@matchesState({ sidebar: 'open' })
isSideBarOpen;
@matchesState({ sidebar: { open: 'success' } })
isSuccess;
@matchesState({ sidebar: { open: 'busy' } })
isLoading;
@matchesState({ sidebar: { open: 'error' } })
isError;
@tracked width = null;
@tracked height = null;
@action
handleResize({ target: { scrollWidth: width, scrollHeight: height } }) {
if (width === this.width || height === this.height) return;
this.height = height;
this.width = width;
}
get currentEvalDetail() {
return this.statechart.state.context.evaluation;
}
get hierarchy() {
try {
const data = this.currentEvalDetail?.relatedEvals;
if (data) {
return d3
.stratify()
.id((d) => {
return d.id;
})
.parentId((d) => d.previousEval)([
...data.toArray(),
this.currentEvalDetail,
]);
}
} catch (e) {
console.error(`\n\nRelated Evaluation Error: ${e.message}`);
}
return null;
}
get descendentsMap() {
return this.hierarchy
?.descendants()
.map((d) => d.children)
.compact();
}
get parentEvaluation() {
return this.hierarchy?.data;
}
get error() {
return this.statechart.state.context.error;
}
@action
closeSidebar() {
return this.statechart.send('MODAL_CLOSE');
}
}

View File

@@ -0,0 +1,60 @@
<div class="boxed-section">
<div class="boxed-section-head">
Related Evaluations
</div>
<div class="boxed-section-body related-evaluations" data-test-eval-container>
<div class="sidebar-content" {{on-resize @fns.handleResize}}>
{{#if (and @data.width @data.height)}}
<Providers::ActorsRelationships as |actors|>
<svg
width={{@data.width}}
height="100%"
style="z-index: 10; inset: 0; position: absolute; pointer-events: none"
{{did-update actors.fns.recalcCurves @data.width}}
>
{{#each actors.data.relationships as |r|}}
<path
d={{r.d}}
stroke="#7E8FA8"
strokeWidth="1"
fill="none"
></path>
<circle
cx={{r.sx}}
cy={{r.sy}}
r="4"
fill="white"
stroke="black"
></circle>
<circle
cx={{r.ex}}
cy={{r.ey}}
r="4"
fill="white"
stroke="black"
></circle>
{{/each}}
</svg>
</Providers::ActorsRelationships>
{{/if}}
<div>
<EvaluationSidebar::EvaluationActor
@eval={{@data.parentEvaluation}}
@activeEvaluationID={{@data.activeEvaluationID}}
@onClick={{fn @fns.handleEvaluationClick @data.parentEvaluation}}
/>
</div>
{{#each @data.descendentsMap as |evals|}}
<div class="evaluation-actors">
{{#each evals as |eval|}}
<EvaluationSidebar::EvaluationActor
@eval={{eval.data}}
@activeEvaluationID={{@data.activeEvaluationID}}
@onClick={{fn @fns.handleEvaluationClick eval.data}}
/>
{{/each}}
</div>
{{/each}}
</div>
</div>
</div>

View File

@@ -0,0 +1,3 @@
{{yield
(hash fns=this.actorsRelationships.fns data=this.actorsRelationships.data)
}}

View File

@@ -0,0 +1,6 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
export default class ActorsRelationships extends Component {
@service actorsRelationships;
}

View File

@@ -1,12 +1,82 @@
import { getOwner } from '@ember/application';
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { schedule } from '@ember/runloop';
import { inject as service } from '@ember/service';
import { useMachine } from 'ember-statecharts';
import { use } from 'ember-usable';
import evaluationsMachine from '../../machines/evaluations';
export default class EvaluationsController extends Controller {
@service store;
@service userSettings;
queryParams = ['nextToken', 'pageSize', 'status'];
// We use statecharts here to manage complex user flows for the sidebar logic
@use
statechart = useMachine(evaluationsMachine).withConfig({
services: {
loadEvaluation: this.loadEvaluation,
},
actions: {
updateEvaluationQueryParameter: this.updateEvaluationQueryParameter,
removeCurrentEvaluationQueryParameter:
this.removeCurrentEvaluationQueryParameter,
},
guards: {
sidebarIsOpen: this._sidebarIsOpen,
},
});
queryParams = ['nextToken', 'currentEval', 'pageSize', 'status'];
@tracked currentEval = null;
@action
_sidebarIsOpen() {
return !!this.currentEval;
}
@action
async loadEvaluation(context, { evaluation }) {
let evaluationId;
if (evaluation?.id) {
evaluationId = evaluation.id;
} else {
evaluationId = this.currentEval;
}
return this.store.findRecord('evaluation', evaluationId, {
reload: true,
adapterOptions: { related: true },
});
}
@action
async handleEvaluationClick(evaluation, e) {
if (
e instanceof MouseEvent ||
(e instanceof KeyboardEvent && (e.code === 'Enter' || e.code === 'Space'))
) {
this.statechart.send('LOAD_EVALUATION', { evaluation });
}
}
@action
notifyEvalChange([evaluation]) {
schedule('actions', this, () => {
this.statechart.send('CHANGE_EVAL', { evaluation });
});
}
@action
updateEvaluationQueryParameter(context, { evaluation }) {
this.currentEval = evaluation.id;
}
@action
removeCurrentEvaluationQueryParameter() {
this.currentEval = null;
}
get shouldDisableNext() {
return !this.model.meta?.nextToken;
@@ -52,6 +122,12 @@ export default class EvaluationsController extends Controller {
@action
refresh() {
const isDefaultParams = this.nextToken === null && this.status === null;
if (isDefaultParams) {
getOwner(this).lookup('route:evaluations.index').refresh();
return;
}
this._resetTokens();
this.status = null;
this.pageSize = this.userSettings.pageSize;

View File

@@ -0,0 +1,124 @@
import { assign, createMachine, send } from 'xstate';
// Docs on using statecharts: https://xstate.js.org/docs/packages/xstate-fsm/#api
export default createMachine(
{
id: 'evaluations_ui',
context: { evaluation: null },
type: 'parallel',
states: {
table: {
initial: 'unknown',
on: {
NEXT: {
actions: ['requestNextPage', send('MODAL_CLOSE')],
},
PREV: {
actions: ['requestPrevPage', send('MODAL_CLOSE')],
},
CHANGE_PAGES_SIZE: {
actions: ['changePageSize', send('MODAL_CLOSE')],
},
MODEL_UPDATED: '#unknown',
},
states: {
unknown: {
id: 'unknown',
always: [{ target: 'data', cond: 'hasData' }, { target: 'empty' }],
},
data: {},
empty: {},
},
},
sidebar: {
initial: 'unknown',
states: {
unknown: {
always: [
{ target: 'open', cond: 'sidebarIsOpen' },
{ target: 'close' },
],
},
open: {
initial: 'busy',
exit: ['removeCurrentEvaluationQueryParameter'],
states: {
busy: {
invoke: {
src: 'loadEvaluation',
onDone: 'success',
onError: 'error',
},
},
success: {
entry: assign({
evaluation: (context, event) => {
return event.data;
},
}),
on: {
LOAD_EVALUATION: {
target: 'busy',
actions: ['updateEvaluationQueryParameter'],
},
},
},
error: {
entry: assign({ error: (_ctx, event) => event.data }),
on: {
RETRY: 'busy',
},
},
},
on: {
MODAL_CLOSE: 'close',
CHANGE_EVAL: [{ target: 'close', cond: 'hasNoCurrentEval' }],
},
},
close: {
on: {
LOAD_EVALUATION: {
target: 'open',
actions: ['updateEvaluationQueryParameter'],
},
CHANGE_EVAL: [
{
target: 'open',
cond: 'hasCurrentEval',
},
],
},
},
},
},
},
},
{
services: {
// Overridden in the controller
async loadEvaluations() {},
async loadEvaluation() {},
},
guards: {
sidebarIsOpen() {
return false;
},
hasData() {
return true;
},
hasNoCurrentEval(_ctx, { evaluation }) {
return !evaluation;
},
hasCurrentEval(_ctx, { evaluation }) {
return evaluation;
},
notBusy(_ctx, _event, meta) {
return !meta.state.matches({ sidebar: { open: 'busy' } });
},
},
actions: {
updateEvaluationQueryParameter() {},
removeCurrentEvaluationQueryParameter() {},
},
}
);

View File

@@ -0,0 +1,23 @@
import Model, { attr } from '@ember-data/model';
import shortUUIDProperty from '../utils/properties/short-uuid';
export default class EvaluationStub extends Model {
@shortUUIDProperty('id') shortId;
@attr('number') priority;
@attr('string') type;
@attr('string') triggeredBy;
@attr('string') namespace;
@attr('string') jobId;
@attr('string') nodeId;
@attr('string') deploymentId;
@attr('string') status;
@attr('string') statusDescription;
@attr('date') waitUntil;
@attr('string') previousEval;
@attr('string') nextEval;
@attr('string') blockedEval;
@attr('number') modifyIndex;
@attr('date') modifyTime;
@attr('number') createIndex;
@attr('date') createTime;
}

View File

@@ -1,6 +1,6 @@
import { bool, equal } from '@ember/object/computed';
import Model from '@ember-data/model';
import { attr, belongsTo } from '@ember-data/model';
import { attr, belongsTo, hasMany } from '@ember-data/model';
import { fragmentArray } from 'ember-data-model-fragments/attributes';
import shortUUIDProperty from '../utils/properties/short-uuid';
@@ -15,6 +15,11 @@ export default class Evaluation extends Model {
@fragmentArray('placement-failure', { defaultValue: () => [] })
failedTGAllocs;
@attr('string') previousEval;
@attr('string') nextEval;
@attr('string') blockedEval;
@hasMany('evaluation-stub', { async: false }) relatedEvals;
@bool('failedTGAllocs.length') hasPlacementFailures;
@equal('status', 'blocked') isBlocked;

View File

@@ -15,6 +15,37 @@ export default class Evaluation extends ApplicationSerializer {
hash.Namespace = hash.Namespace || get(hash, 'Job.Namespace') || 'default';
hash.JobID = JSON.stringify([hash.JobID, hash.Namespace]);
return super.normalize(typeHash, hash);
const relatedEvals = hash.RelatedEvals;
const normalizedHash = super.normalize(typeHash, hash);
if (relatedEvals?.length) {
this._handleRelatedEvalsRelationshipData(relatedEvals, normalizedHash);
}
return normalizedHash;
}
_handleRelatedEvalsRelationshipData(relatedEvals, normalizedHash) {
normalizedHash.data.relationships = normalizedHash.data.relationships || {};
normalizedHash.data.relationships.relatedEvals = {
data: relatedEvals.map((evaluationStub) => {
return { id: evaluationStub.ID, type: 'evaluation-stub' };
}),
};
normalizedHash.included = normalizedHash.included || [];
const included = relatedEvals.reduce((acc, evaluationStub) => {
const jsonDocument = this.normalize(
this.store.modelFor('evaluation-stub'),
evaluationStub
);
return [...acc, jsonDocument.data];
}, normalizedHash.included);
normalizedHash.included = included;
}
}

View File

@@ -0,0 +1,101 @@
import Service from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { schedule } from '@ember/runloop';
import { getBoxToBoxArrow } from 'curved-arrows';
function boxToArrow(ra, rb) {
const bbA = ra;
const bbB = rb;
const [sx, sy, c1x, c1y, c2x, c2y, ex, ey, ae, as] = getBoxToBoxArrow(
bbA.offsetLeft,
bbA.offsetTop,
bbA.offsetWidth,
bbA.offsetHeight,
bbB.offsetLeft,
bbB.offsetTop,
bbB.offsetWidth,
bbB.offsetHeight
);
return {
sx,
sy,
c1x,
c1y,
c2x,
c2y,
ex,
ey,
ae,
as,
};
}
export default class ActorRelationshipService extends Service {
@tracked actors = [];
get fns() {
const { registerActor, deregisterActor, recalcCurves } = this;
return { registerActor, deregisterActor, recalcCurves };
}
get data() {
const { actors, relationships } = this;
return { actors, relationships };
}
@action registerActor(actor) {
schedule('actions', this, () => {
this.actors = [...this.actors, actor];
});
}
@action deregisterActor(actor) {
schedule('actions', this, () => {
this.actors = this.actors.filter((a) => a !== actor);
});
}
get rects() {
const { actors } = this;
return actors
.filter((e) => e.previousEval)
.map((e) => {
const { previousEval: pid, id } = e;
const eRectangle = document.querySelector(`[data-eval="${id}"]`);
const prevRectangle = document.querySelector(`[data-eval="${pid}"]`);
return [eRectangle, prevRectangle];
});
}
get relationships() {
const { rects } = this;
return rects.map(([eRectangle, prevRectangle]) => {
const { sx, sy, c1x, c1y, c2x, c2y, ex, ey } = boxToArrow(
eRectangle,
prevRectangle
);
return {
d: `M ${sx} ${sy} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${ex} ${ey}`,
sx,
sy,
ex,
ey,
};
});
}
@action
recalcCurves() {
// retrigger the tracked getters by resetting dependent keys
/* eslint-disable-next-line */
this.actors = this.actors;
}
}

View File

@@ -36,6 +36,7 @@
@import './components/recommendation-card';
@import './components/recommendation-row';
@import './components/search-box';
@import './components/sidebar';
@import './components/simple-list';
@import './components/status-text';
@import './components/stepper-input';
@@ -44,3 +45,4 @@
@import './components/toolbar';
@import './components/tooltip';
@import './components/two-step-button';
@import './components/evaluations';

View File

@@ -0,0 +1,9 @@
.related-evaluation {
margin: 24px;
outline: 1px solid #d9dee6;
padding: 10px;
width: 100px;
&.is-active {
background-color: whitesmoke;
}
}

View File

@@ -0,0 +1,52 @@
.sidebar {
position: fixed;
background: #ffffff;
width: 750px;
padding: 24px;
right: 0%;
overflow-y: auto;
bottom: 0;
top: 112px;
transform: translateX(100%);
transition-duration: 150ms;
transition-timing-function: ease;
box-shadow: 6px 6px rgba(0, 0, 0, 0.06), 0px 12px 16px rgba(0, 0, 0, 0.2);
z-index: 10;
&.open {
transform: translateX(0%);
}
}
.sidebar-content {
position: relative;
display: flex;
}
.error-header {
display: flex;
justify-content: flex-end;
}
.detail-header {
display: flex;
justify-content: space-between;
}
.related-evaluations {
overflow-x: scroll;
overflow-y: hidden;
}
.evaluation-actors {
display: flex;
flex-direction: column;
justify-content: space-evenly;
flex-basis: 100%;
}
.actor {
margin: 24px;
outline: 1px solid #d9dee6;
padding: 10px;
width: 100px;
}

View File

@@ -0,0 +1,23 @@
<Providers::ActorsRelationships as |actors|>
<div
class="related-evaluation
{{if (eq @eval.id @activeEvaluationID) "is-active"}}"
data-eval={{@eval.id}}
...attributes
{{did-insert (fn actors.fns.registerActor @eval)}}
{{will-destroy (fn actors.fns.deregisterActor @eval)}}
>
{{#if (eq @eval.id @activeEvaluationID)}}
<span data-test-rel-eval={{@eval.id}}>
{{@eval.shortId}}
</span>
{{else}}
<a data-test-rel-eval={{@eval.id}} {{on "click" @onClick}}>
{{@eval.shortId}}
</a>
{{/if}}
<span>
<StatusCell @status={{@eval.status}} />
</span>
</div>
</Providers::ActorsRelationships>

View File

@@ -1,4 +1,7 @@
<div data-test-gutter-menu class="page-column is-left {{if this.isOpen "is-open"}}">
<div
data-test-gutter-menu
class="page-column is-left {{if this.isOpen "is-open"}}"
>
<div class="gutter {{if this.isOpen "is-open"}}">
<header class="collapsed-menu {{if this.isOpen "is-open"}}">
<span
@@ -34,13 +37,21 @@
</p>
<ul class="menu-list">
<li>
<LinkTo @route="jobs" @activeClass="is-active" data-test-gutter-link="jobs">
<LinkTo
@route="jobs"
@activeClass="is-active"
data-test-gutter-link="jobs"
>
Jobs
</LinkTo>
</li>
{{#if (can "accept recommendation")}}
<li>
<LinkTo @route="optimize" @activeClass="is-active" data-test-gutter-link="optimize">
<LinkTo
@route="optimize"
@activeClass="is-active"
data-test-gutter-link="optimize"
>
Optimize
</LinkTo>
</li>
@@ -51,7 +62,11 @@
</p>
<ul class="menu-list">
<li>
<LinkTo @route="csi" @activeClass="is-active" data-test-gutter-link="storage">
<LinkTo
@route="csi"
@activeClass="is-active"
data-test-gutter-link="storage"
>
Storage
<span class="tag is-small">
Beta
@@ -64,17 +79,29 @@
</p>
<ul class="menu-list">
<li>
<LinkTo @route="clients" @activeClass="is-active" data-test-gutter-link="clients">
<LinkTo
@route="clients"
@activeClass="is-active"
data-test-gutter-link="clients"
>
Clients
</LinkTo>
</li>
<li>
<LinkTo @route="servers" @activeClass="is-active" data-test-gutter-link="servers">
<LinkTo
@route="servers"
@activeClass="is-active"
data-test-gutter-link="servers"
>
Servers
</LinkTo>
</li>
<li>
<LinkTo @route="topology" @activeClass="is-active" data-test-gutter-link="topology">
<LinkTo
@route="topology"
@activeClass="is-active"
data-test-gutter-link="topology"
>
Topology
</LinkTo>
</li>
@@ -84,7 +111,11 @@
</p>
<ul class="menu-list">
<li>
<LinkTo @route="evaluations" @activeClass="is-active" data-test-gutter-link="evaluations">
<LinkTo
@route="evaluations"
@activeClass="is-active"
data-test-gutter-link="evaluations"
>
Evaluations
</LinkTo>
</li>

View File

@@ -1,3 +1,4 @@
<PageLayout>
<PortalTarget @name="eval-detail-portal" />
{{outlet}}
</PageLayout>

View File

@@ -1,138 +1,162 @@
{{page-title "Evaluations"}}
{{did-update this.notifyEvalChange this.currentEval}}
<section class="section">
<div class="toolbar">
<div class="toolbar-item is-right-aligned">
<SingleSelectDropdown
data-test-evaluation-status-facet
@label="Status"
@options={{this.optionsEvaluationsStatus}}
@selection={{this.status}}
@onSelect={{action this.setStatus}}
/>
<div class="table-container">
<div class="toolbar">
<div class="toolbar-item is-right-aligned">
<SingleSelectDropdown
data-test-evaluation-status-facet
@label="Status"
@options={{this.optionsEvaluationsStatus}}
@selection={{this.status}}
@onSelect={{action this.setStatus}}
/>
</div>
</div>
{{#if @model.length}}
<ListTable data-test-eval-table @source={{@model}} as |t|>
<t.head>
<th>
Evaluation ID
</th>
<th>
Resource
</th>
<th>
Priority
</th>
<th>
Created
</th>
<th>
Triggered By
</th>
<th>
Status
</th>
<th>
Placement Failures
</th>
</t.head>
<t.body as |row|>
<tr
data-test-evaluation="{{row.model.shortId}}"
style="cursor: pointer;"
class="{{if (eq this.currentEval row.model.id) "is-active"}}"
tabindex="0"
{{on "click" (fn this.handleEvaluationClick row.model)}}
{{on "keyup" (fn this.handleEvaluationClick row.model)}}
>
<td data-test-id>
{{row.model.shortId}}
</td>
<td data-test-id>
{{#if row.model.hasJob}}
<LinkTo
data-test-evaluation-resource
@model={{concat row.model.plainJobId "@" row.model.namespace}}
@route="jobs.job"
>
{{row.model.plainJobId}}
</LinkTo>
{{else}}
<LinkTo
data-test-evaluation-resource
@model={{row.model.nodeId}}
@route="clients.client"
>
{{row.model.shortNodeId}}
</LinkTo>
{{/if}}
</td>
<td data-test-priority>
{{row.model.priority}}
</td>
<td data-test-create-time>
{{format-month-ts row.model.createTime}}
</td>
<td data-test-triggered-by>
{{row.model.triggeredBy}}
</td>
<td data-test-status class="is-one-line">
<StatusCell @status={{row.model.status}} />
</td>
<td data-test-blocked>
{{#if (eq row.model.status "blocked")}}
N/A - In Progress
{{else if row.model.hasPlacementFailures}}
True
{{else}}
False
{{/if}}
</td>
</tr>
</t.body>
</ListTable>
<div class="table-foot with-padding">
<PageSizeSelect data-test-per-page @onChange={{this.onChange}} />
<div>
<button
class="button"
data-test-eval-refresh
type="button"
{{on "click" this.refresh}}
>
{{x-icon "refresh-default" class="is-text"}}
Refresh
</button>
<button
data-test-eval-pagination-prev
type="button"
class="button is-text is-borderless"
disabled={{this.shouldDisablePrev}}
{{on "click" (fn this.onPrev this.lastToken)}}
>
{{x-icon "chevron-left" class="is-large"}}
</button>
<button
data-test-eval-pagination-next
type="button"
class="button is-text is-borderless"
disabled={{this.shouldDisableNext}}
{{on "click" (fn this.onNext @model.meta.nextToken)}}
>
{{x-icon "chevron-right" class="is-large"}}
</button>
</div>
</div>
{{else}}
<div class="boxed-section-body">
<div class="empty-message" data-test-empty-evaluations-list>
<h3
class="empty-message-headline"
data-test-empty-evalations-list-headline
>
No Matches
</h3>
<p class="empty-message-body">
{{#if this.status}}
<span data-test-no-eval-match>
No evaluations match the status
<strong>
{{this.status}}
</strong>
</span>
{{else}}
<span data-test-no-eval>
There are no evaluations
</span>
{{/if}}
</p>
</div>
</div>
{{/if}}
</div>
{{#if @model.length}}
<ListTable data-test-eval-table @source={{@model}} as |t|>
<t.head>
<th>
Evaluation ID
</th>
<th>
Resource
</th>
<th>
Priority
</th>
<th>
Created
</th>
<th>
Triggered By
</th>
<th>
Status
</th>
<th>
Placement Failures
</th>
</t.head>
<t.body as |row|>
<tr data-test-evaluation="{{row.model.shortId}}">
<td data-test-id>
{{row.model.shortId}}
</td>
<td data-test-id>
{{#if row.model.hasJob}}
<LinkTo
data-test-evaluation-resource
@model={{row.model.plainJobId}}
@route="jobs.job"
@query={{hash namespace=row.model.namespace}}
>
{{row.model.plainJobId}}
</LinkTo>
{{else}}
<LinkTo
data-test-evaluation-resource
@model={{row.model.nodeId}}
@route="clients.client"
>
{{row.model.shortNodeId}}
</LinkTo>
{{/if}}
</td>
<td data-test-priority>
{{row.model.priority}}
</td>
<td data-test-create-time>
{{format-month-ts row.model.createTime}}
</td>
<td data-test-triggered-by>
{{row.model.triggeredBy}}
</td>
<td data-test-status class="is-one-line">
<StatusCell @status={{row.model.status}} />
</td>
<td data-test-blocked>
{{#if (eq row.model.status "blocked")}}
N/A - In Progress
{{else if row.model.hasPlacementFailures}}
True
{{else}}
False
{{/if}}
</td>
</tr>
</t.body>
</ListTable>
<div class="table-foot with-padding">
<PageSizeSelect data-test-per-page @onChange={{this.onChange}} />
<div>
<button class="button" data-test-eval-refresh type="button" {{on "click" this.refresh}}>
{{x-icon "refresh-default" class="is-text"}}
Refresh
</button>
<button
data-test-eval-pagination-prev
type="button"
class="button is-text is-borderless"
disabled={{this.shouldDisablePrev}}
{{on "click" (fn this.onPrev this.lastToken)}}
>
{{x-icon "chevron-left" class="is-large"}}
</button>
<button
data-test-eval-pagination-next
type="button"
class="button is-text is-borderless"
disabled={{this.shouldDisableNext}}
{{on "click" (fn this.onNext @model.meta.nextToken)}}
>
{{x-icon "chevron-right" class="is-large"}}
</button>
</div>
</div>
{{else}}
<div class="boxed-section-body">
<div class="empty-message" data-test-empty-evaluations-list>
<h3 class="empty-message-headline" data-test-empty-evalations-list-headline>
No Matches
</h3>
<p class="empty-message-body">
{{#if this.status}}
<span data-test-no-eval-match>
No evaluations match the status
<strong>
{{this.status}}
</strong>
</span>
{{else}}
<span data-test-no-eval>
There are no evaluations
</span>
{{/if}}
</p>
</div>
</div>
{{/if}}
<EvaluationSidebar::Detail
@statechart={{this.statechart}}
@fns={{hash
closeSidebar=this.closeSidebar
handleEvaluationClick=this.handleEvaluationClick
}}
/>
</section>

View File

@@ -6,6 +6,7 @@ import { generateDiff } from './factories/job-version';
import { generateTaskGroupFailures } from './factories/evaluation';
import { copy } from 'ember-copy';
import formatHost from 'nomad-ui/utils/format-host';
import { generateAcceptanceTestEvalMock } from './utils';
export function findLeader(schema) {
const agent = schema.agents.first();
@@ -262,7 +263,19 @@ export default function () {
});
this.get('/evaluations');
this.get('/evaluation/:id');
this.get(
'/evaluation/:id',
function ({ evaluations }, { params, queryParams }) {
const showRelated = queryParams.related;
if (showRelated) {
// we are dealing with a "related" request - return the mock
return generateAcceptanceTestEvalMock(params.id);
}
return evaluations.find(params.id);
}
);
this.get('/deployment/allocations/:id', function (schema, { params }) {
const job = schema.jobs.find(schema.deployments.find(params.id).jobId);

View File

@@ -4,9 +4,7 @@ export function provide(count, provider) {
if (typeof count === 'function') {
count = count();
}
return Array(count)
.fill(null)
.map(provider);
return Array(count).fill(null).map(provider);
}
export function provider() {
@@ -26,3 +24,467 @@ export function arrToObj(prop, alias = '') {
return obj;
};
}
export const generateAcceptanceTestEvalMock = (id) => {
return {
CreateIndex: 20,
CreateTime: 1647899150314738000,
ID: id,
JobID: 'example',
JobModifyIndex: 10,
ModifyIndex: 31,
ModifyTime: 1647899318007569000,
Namespace: 'default',
NextEval: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef',
PreviousEval: 'd8a5c14f-120a-3d83-6305-90927356dd6c',
Priority: 50,
RelatedEvals: [
{
BlockedEval: '',
CreateIndex: 31,
CreateTime: 1647899318007563000,
DeploymentID: '',
ID: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef',
JobID: 'example',
ModifyIndex: 44,
ModifyTime: 1647899591412413000,
Namespace: 'default',
NextEval: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
NodeID: '',
PreviousEval: id,
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 31,
CreateTime: 1647899318007563000,
DeploymentID: '',
ID: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef-deux',
JobID: 'example',
ModifyIndex: 44,
ModifyTime: 1647899591412413000,
Namespace: 'default',
NextEval: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
NodeID: '',
PreviousEval: id,
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 10,
CreateTime: 1647899129298997000,
DeploymentID: '',
ID: 'd8a5c14f-120a-3d83-6305-90927356dd6c',
JobID: 'example',
ModifyIndex: 20,
ModifyTime: 1647899150314745000,
Namespace: 'default',
NextEval: id,
NodeID: '',
PreviousEval: '',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'job-register',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 44,
CreateTime: 1647899591412410000,
DeploymentID: '',
ID: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
JobID: 'example',
ModifyIndex: 53,
ModifyTime: 1647899729480596000,
Namespace: 'default',
NextEval: 'e49bf53c-da6a-c869-8317-f2089682f503',
NodeID: '',
PreviousEval: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 53,
CreateTime: 1647899729480592000,
DeploymentID: '',
ID: 'e49bf53c-da6a-c869-8317-f2089682f503',
JobID: 'example',
ModifyIndex: 64,
ModifyTime: 1647899881302731000,
Namespace: 'default',
NextEval: 'a8d29cfc-517c-2e4c-9722-b47e84152c64',
NodeID: '',
PreviousEval: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 64,
CreateTime: 1647899881302723000,
DeploymentID: '',
ID: 'a8d29cfc-517c-2e4c-9722-b47e84152c64',
JobID: 'example',
ModifyIndex: 81,
ModifyTime: 1647900212725381000,
Namespace: 'default',
NextEval: 'b37d06e4-4eb4-b29d-3b4a-b0c7bf2528ad',
NodeID: '',
PreviousEval: 'e49bf53c-da6a-c869-8317-f2089682f503',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 81,
CreateTime: 1647900212725376000,
DeploymentID: '',
ID: 'b37d06e4-4eb4-b29d-3b4a-b0c7bf2528ad',
JobID: 'example',
ModifyIndex: 97,
ModifyTime: 1647900516944239000,
Namespace: 'default',
NextEval: 'd7c50aa5-5bf1-5119-d7e7-0d0ae5381856',
NodeID: '',
PreviousEval: 'a8d29cfc-517c-2e4c-9722-b47e84152c64',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 97,
CreateTime: 1647900516944236000,
DeploymentID: '',
ID: 'd7c50aa5-5bf1-5119-d7e7-0d0ae5381856',
JobID: 'example',
ModifyIndex: 114,
ModifyTime: 1647900825385587000,
Namespace: 'default',
NextEval: 'ea2239aa-26d6-8874-8c56-e1600585772b',
NodeID: '',
PreviousEval: 'b37d06e4-4eb4-b29d-3b4a-b0c7bf2528ad',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 114,
CreateTime: 1647900825385584000,
DeploymentID: '',
ID: 'ea2239aa-26d6-8874-8c56-e1600585772b',
JobID: 'example',
ModifyIndex: 128,
ModifyTime: 1647900979511304000,
Namespace: 'default',
NextEval: '25a2dd19-8d22-d1dd-280a-79860c9b8bdb',
NodeID: '',
PreviousEval: 'd7c50aa5-5bf1-5119-d7e7-0d0ae5381856',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 128,
CreateTime: 1647900979511301000,
DeploymentID: '',
ID: '25a2dd19-8d22-d1dd-280a-79860c9b8bdb',
JobID: 'example',
ModifyIndex: 136,
ModifyTime: 1647901211369652000,
Namespace: 'default',
NextEval: '1fded690-20ad-6afa-3b89-59e319dfce18',
NodeID: '',
PreviousEval: 'ea2239aa-26d6-8874-8c56-e1600585772b',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 136,
CreateTime: 1647901211369648000,
DeploymentID: '',
ID: '1fded690-20ad-6afa-3b89-59e319dfce18',
JobID: 'example',
ModifyIndex: 136,
ModifyTime: 1647901211369648000,
Namespace: 'default',
NextEval: '',
NodeID: '',
PreviousEval: '25a2dd19-8d22-d1dd-280a-79860c9b8bdb',
Priority: 50,
Status: 'pending',
StatusDescription: '',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
],
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
Wait: 20000000000,
};
};
export const MOCK_EVALUATION = {
CreateIndex: 20,
CreateTime: 1647899150314738000,
ID: 'fede162c-26a6-c108-178b-1c140f9f5680',
JobID: 'example',
JobModifyIndex: 10,
ModifyIndex: 31,
ModifyTime: 1647899318007569000,
Namespace: 'default',
NextEval: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef',
PreviousEval: 'd8a5c14f-120a-3d83-6305-90927356dd6c',
Priority: 50,
RelatedEvals: [
{
BlockedEval: '',
CreateIndex: 31,
CreateTime: 1647899318007563000,
DeploymentID: '',
ID: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef',
JobID: 'example',
ModifyIndex: 44,
ModifyTime: 1647899591412413000,
Namespace: 'default',
NextEval: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
NodeID: '',
PreviousEval: 'fede162c-26a6-c108-178b-1c140f9f5680',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 10,
CreateTime: 1647899129298997000,
DeploymentID: '',
ID: 'd8a5c14f-120a-3d83-6305-90927356dd6c',
JobID: 'example',
ModifyIndex: 20,
ModifyTime: 1647899150314745000,
Namespace: 'default',
NextEval: 'fede162c-26a6-c108-178b-1c140f9f5680',
NodeID: '',
PreviousEval: '',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'job-register',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 44,
CreateTime: 1647899591412410000,
DeploymentID: '',
ID: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
JobID: 'example',
ModifyIndex: 53,
ModifyTime: 1647899729480596000,
Namespace: 'default',
NextEval: 'e49bf53c-da6a-c869-8317-f2089682f503',
NodeID: '',
PreviousEval: 'fd1cd898-d655-c7e4-17f6-a1a2e98b18ef',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 53,
CreateTime: 1647899729480592000,
DeploymentID: '',
ID: 'e49bf53c-da6a-c869-8317-f2089682f503',
JobID: 'example',
ModifyIndex: 64,
ModifyTime: 1647899881302731000,
Namespace: 'default',
NextEval: 'a8d29cfc-517c-2e4c-9722-b47e84152c64',
NodeID: '',
PreviousEval: 'cac7dfa0-b79b-ee55-c86a-0ca89dffb9e1',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 64,
CreateTime: 1647899881302723000,
DeploymentID: '',
ID: 'a8d29cfc-517c-2e4c-9722-b47e84152c64',
JobID: 'example',
ModifyIndex: 81,
ModifyTime: 1647900212725381000,
Namespace: 'default',
NextEval: 'b37d06e4-4eb4-b29d-3b4a-b0c7bf2528ad',
NodeID: '',
PreviousEval: 'e49bf53c-da6a-c869-8317-f2089682f503',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 81,
CreateTime: 1647900212725376000,
DeploymentID: '',
ID: 'b37d06e4-4eb4-b29d-3b4a-b0c7bf2528ad',
JobID: 'example',
ModifyIndex: 97,
ModifyTime: 1647900516944239000,
Namespace: 'default',
NextEval: 'd7c50aa5-5bf1-5119-d7e7-0d0ae5381856',
NodeID: '',
PreviousEval: 'a8d29cfc-517c-2e4c-9722-b47e84152c64',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 97,
CreateTime: 1647900516944236000,
DeploymentID: '',
ID: 'd7c50aa5-5bf1-5119-d7e7-0d0ae5381856',
JobID: 'example',
ModifyIndex: 114,
ModifyTime: 1647900825385587000,
Namespace: 'default',
NextEval: 'ea2239aa-26d6-8874-8c56-e1600585772b',
NodeID: '',
PreviousEval: 'b37d06e4-4eb4-b29d-3b4a-b0c7bf2528ad',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 114,
CreateTime: 1647900825385584000,
DeploymentID: '',
ID: 'ea2239aa-26d6-8874-8c56-e1600585772b',
JobID: 'example',
ModifyIndex: 128,
ModifyTime: 1647900979511304000,
Namespace: 'default',
NextEval: '25a2dd19-8d22-d1dd-280a-79860c9b8bdb',
NodeID: '',
PreviousEval: 'd7c50aa5-5bf1-5119-d7e7-0d0ae5381856',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 128,
CreateTime: 1647900979511301000,
DeploymentID: '',
ID: '25a2dd19-8d22-d1dd-280a-79860c9b8bdb',
JobID: 'example',
ModifyIndex: 136,
ModifyTime: 1647901211369652000,
Namespace: 'default',
NextEval: '1fded690-20ad-6afa-3b89-59e319dfce18',
NodeID: '',
PreviousEval: 'ea2239aa-26d6-8874-8c56-e1600585772b',
Priority: 50,
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
{
BlockedEval: '',
CreateIndex: 136,
CreateTime: 1647901211369648000,
DeploymentID: '',
ID: '1fded690-20ad-6afa-3b89-59e319dfce18',
JobID: 'example',
ModifyIndex: 136,
ModifyTime: 1647901211369648000,
Namespace: 'default',
NextEval: '',
NodeID: '',
PreviousEval: '25a2dd19-8d22-d1dd-280a-79860c9b8bdb',
Priority: 50,
Status: 'pending',
StatusDescription: '',
TriggeredBy: 'failed-follow-up',
Type: 'service',
WaitUntil: null,
},
],
Status: 'failed',
StatusDescription: 'evaluation reached delivery limit (3)',
TriggeredBy: 'failed-follow-up',
Type: 'service',
Wait: 20000000000,
};

View File

@@ -40,6 +40,7 @@
"devDependencies": {
"@babel/plugin-proposal-object-rest-spread": "^7.4.3",
"@ember/optional-features": "2.0.0",
"@ember/render-modifiers": "^2.0.4",
"@ember/test-helpers": "^2.6.0",
"@glimmer/component": "^1.0.4",
"@glimmer/tracking": "^1.0.4",
@@ -79,6 +80,7 @@
"ember-cli-sri": "^2.1.1",
"ember-cli-string-helpers": "^6.1.0",
"ember-cli-terser": "^4.0.2",
"ember-click-outside": "^3.0.0",
"ember-composable-helpers": "^5.0.0",
"ember-concurrency": "^2.2.1",
"ember-copy": "^2.0.1",
@@ -95,6 +97,7 @@
"ember-modifier": "^3.1.0",
"ember-moment": "^9.0.1",
"ember-named-blocks-polyfill": "^0.2.4",
"ember-on-resize-modifier": "^1.0.0",
"ember-overridable-computed": "^1.0.0",
"ember-page-title": "^6.2.2",
"ember-power-select": "^4.1.7",
@@ -104,6 +107,8 @@
"ember-responsive": "^4.0.2",
"ember-sinon": "^5.0.0",
"ember-source": "~3.28.8",
"ember-stargate": "^0.4.1",
"ember-statecharts": "^0.13.2",
"ember-template-lint": "^3.15.0",
"ember-test-selectors": "^6.0.0",
"ember-truth-helpers": "^3.0.0",
@@ -165,6 +170,8 @@
},
"dependencies": {
"codemirror": "^5.56.0",
"curved-arrows": "^0.1.0",
"d3": "^7.3.0",
"lru_map": "^0.4.1",
"no-case": "^3.0.4",
"title-case": "^3.0.3"

View File

@@ -1,4 +1,11 @@
import { click, currentRouteName, visit } from '@ember/test-helpers';
import {
click,
currentRouteName,
currentURL,
visit,
waitFor,
waitUntil,
} from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
@@ -8,6 +15,7 @@ import {
selectChoose,
clickTrigger,
} from 'ember-power-select/test-support/helpers';
import { generateAcceptanceTestEvalMock } from '../../mirage/utils';
const getStandardRes = () => [
{
@@ -407,16 +415,17 @@ module('Acceptance | evaluations list', function (hooks) {
module('resource linking', function () {
test('it should generate a link to the job resource', async function (assert) {
server.create('node');
const job = server.create('job', { shallow: true });
const job = server.create('job', { id: 'example', shallow: true });
server.create('evaluation', { jobId: job.id });
await visit('/evaluations');
await visit('/evaluations');
assert
.dom('[data-test-evaluation-resource]')
.hasText(
job.name,
'It conditionally renders the correct resource name'
);
await click('[data-test-evaluation-resource]');
assert
.dom('[data-test-job-name]')
@@ -443,4 +452,106 @@ module('Acceptance | evaluations list', function (hooks) {
.includesText(node.name, 'We navigate to the correct client page.');
});
});
module('evaluation detail', function () {
test('clicking an evaluation opens the detail view', async function (assert) {
server.get('/evaluations', getStandardRes);
server.get('/evaluation/:id', function (_, { params }) {
return { ...generateAcceptanceTestEvalMock(params.id), ID: params.id };
});
await visit('/evaluations');
const evalId = '5fb1b8cd';
await click(`[data-test-evaluation='${evalId}']`);
assert
.dom('[data-test-eval-detail-is-open]')
.exists(
'A sidebar portal mounts to the dom after clicking an evaluation'
);
assert
.dom('[data-test-rel-eval]')
.exists(
{ count: 12 },
'all related evaluations and the current evaluation are displayed'
);
click(`[data-test-rel-eval='fd1cd898-d655-c7e4-17f6-a1a2e98b18ef']`);
await waitFor('[data-test-eval-loading]');
assert
.dom('[data-test-eval-loading]')
.exists(
'transition to loading state after clicking related evaluation'
);
await waitFor('[data-test-eval-detail-header]');
assert.equal(
currentURL(),
'/evaluations?currentEval=fd1cd898-d655-c7e4-17f6-a1a2e98b18ef'
);
assert
.dom('[data-test-title]')
.includesText('fd1cd898', 'New evaluation hash appears in the title');
await click(`[data-test-evaluation='66cb98a6']`);
assert.equal(
currentURL(),
'/evaluations?currentEval=66cb98a6-7740-d5ef-37e4-fa0f8b1de44b',
'Clicking an evaluation in the table updates the sidebar'
);
click('[data-test-eval-sidebar-x]');
// We wait until the sidebar closes since it uses a transition of 300ms
await waitUntil(
() => !document.querySelector('[data-test-eval-detail-is-open]')
);
assert.equal(
currentURL(),
'/evaluations',
'When the user clicks the x button the sidebar closes'
);
});
test('it should provide an error state when loading an invalid evaluation', async function (assert) {
server.get('/evaluations', getStandardRes);
server.get('/evaluation/:id', function () {
return new Response(404, {}, '');
});
await visit('/evaluations');
const evalId = '5fb1b8cd';
await click(`[data-test-evaluation='${evalId}']`);
assert
.dom('[data-test-eval-detail-is-open]')
.exists(
'A sidebar portal mounts to the dom after clicking an evaluation'
);
assert
.dom('[data-test-eval-error]')
.exists(
'all related evaluations and the current evaluation are displayed'
);
click('[data-test-eval-sidebar-x]');
// We wait until the sidebar closes since it uses a transition of 300ms
await waitUntil(
() => !document.querySelector('[data-test-eval-detail-is-open]')
);
assert.equal(
currentURL(),
'/evaluations',
'When the user clicks the x button the sidebar closes'
);
});
});
});

View File

@@ -0,0 +1,137 @@
import { setupMirage } from 'ember-cli-mirage/test-support';
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Integration | Data Modeling | related evaluations', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
test('it should a return a list of related evaluations when the related query parameter is specified', async function (assert) {
assert.expect(2);
const store = this.owner.lookup('service:store');
server.get('/evaluation/:id', function (_, fakeRes) {
assert.equal(
fakeRes.queryParams.related,
'true',
'it should append the related query parameter when making the API request for related evaluations'
);
return {
ID: 'tomster',
Priority: 50,
Type: 'service',
TriggeredBy: 'job-register',
JobID: 'example',
JobModifyIndex: 52,
NodeID: 'yes',
NodeModifyIndex: 0,
Status: 'complete',
StatusDescription: '',
Wait: 0,
NextEval: '',
PreviousEval: '',
BlockedEval: '',
FailedTGAllocs: null,
ClassEligibility: null,
EscapedComputedClass: false,
AnnotatePlan: false,
SnapshotIndex: 53,
QueuedAllocations: {
cache: 0,
},
CreateIndex: 53,
ModifyIndex: 55,
Related: [],
};
});
await store.findRecord('evaluation', 'tomster', {
adapterOptions: { related: true },
});
server.get('/evaluation/:id', function (_, fakeRes) {
assert.notOk(
fakeRes.queryParams.related,
'it should not append the related query parameter when making the API request for related evaluations'
);
return {
ID: 'tomster',
Priority: 50,
Type: 'service',
TriggeredBy: 'job-register',
JobID: 'example',
JobModifyIndex: 52,
NodeID: 'yes',
NodeModifyIndex: 0,
Status: 'complete',
StatusDescription: '',
Wait: 0,
NextEval: '',
PreviousEval: '',
BlockedEval: '',
FailedTGAllocs: null,
ClassEligibility: null,
EscapedComputedClass: false,
AnnotatePlan: false,
SnapshotIndex: 53,
QueuedAllocations: {
cache: 0,
},
CreateIndex: 53,
ModifyIndex: 55,
Related: [],
};
});
await store.findRecord('evaluation', 'tomster');
});
test('it should store related evaluations stubs as a hasMany in the store', async function (assert) {
const store = this.owner.lookup('service:store');
server.get('/evaluation/:id', function () {
return {
ID: 'tomster',
Priority: 50,
Type: 'service',
TriggeredBy: 'job-register',
JobID: 'example',
JobModifyIndex: 52,
NodeID: 'yes',
NodeModifyIndex: 0,
Status: 'complete',
StatusDescription: '',
Wait: 0,
NextEval: '',
PreviousEval: '',
BlockedEval: '',
FailedTGAllocs: null,
ClassEligibility: null,
EscapedComputedClass: false,
AnnotatePlan: false,
SnapshotIndex: 53,
QueuedAllocations: {
cache: 0,
},
CreateIndex: 53,
ModifyIndex: 55,
RelatedEvals: [
{ ID: 'a', StatusDescription: 'a' },
{ ID: 'b', StatusDescription: 'b' },
],
};
});
const result = await store.findRecord('evaluation', 'tomster', {
adapterOptions: { related: true },
});
assert.equal(result.relatedEvals.length, 2);
const mappedResult = result.relatedEvals.map((es) => es.id);
assert.deepEqual(
mappedResult,
['a', 'b'],
'related evals data is accessible'
);
});
});

View File

@@ -2492,7 +2492,7 @@
mkdirp "^1.0.4"
silent-error "^1.1.1"
"@ember/render-modifiers@^2.0.0":
"@ember/render-modifiers@^2.0.0", "@ember/render-modifiers@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.0.4.tgz#0ac7af647cb736076dbfcd54ca71e090cd329d71"
integrity sha512-Zh/fo5VUmVzYHkHVvzWVjJ1RjFUxA2jH0zCp2+DQa80Bf3DUXauiEByxU22UkN4LFT55DBFttC0xCQSJG3WTsg==
@@ -2542,6 +2542,14 @@
ember-cli-version-checker "^5.1.2"
semver "^7.3.5"
"@embroider/addon-shim@^1.0.0", "@embroider/addon-shim@^1.2.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@embroider/addon-shim/-/addon-shim-1.5.0.tgz#639b8b394336a5ae26dd3e24ffc3d34d864ac5ce"
integrity sha512-5zgwA/wTYjgn2Oo6hKRQhF/5Gnwb+hGhj/WXhZQa5yA7fRRdBV1tVMS7b7SLawZcmOhuWkyPwFdgsYtGBvDB0w==
dependencies:
"@embroider/shared-internals" "^1.5.0"
semver "^7.3.5"
"@embroider/core@0.36.0":
version "0.36.0"
resolved "https://registry.yarnpkg.com/@embroider/core/-/core-0.36.0.tgz#fbbd60d29c3fcbe02b4e3e63e6043a43de2b9ce3"
@@ -2647,6 +2655,20 @@
resolve "^1.20.0"
semver "^7.3.2"
"@embroider/macros@^1.2.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@embroider/macros/-/macros-1.5.0.tgz#8c67666359e7814d91cdeac2fd96c896080473b3"
integrity sha512-QqNsWmIJ8LvwWg+YAfB+nfcRZ49hAJNTJAKt2IDpgZDKa/FYj/Mp75UWfJ1BJvB3KUq50OQCOv4TZ3ZcU9h/TQ==
dependencies:
"@embroider/shared-internals" "1.5.0"
assert-never "^1.2.1"
babel-import-util "^1.1.0"
ember-cli-babel "^7.26.6"
find-up "^5.0.0"
lodash "^4.17.21"
resolve "^1.20.0"
semver "^7.3.2"
"@embroider/shared-internals@0.40.0":
version "0.40.0"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-0.40.0.tgz#2f768c60f4f35ba5f9228f046f70324851e8bfe2"
@@ -2699,6 +2721,19 @@
semver "^7.3.5"
typescript-memoize "^1.0.1"
"@embroider/shared-internals@1.5.0", "@embroider/shared-internals@^1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-1.5.0.tgz#d3883f13571be36fcdbc59c65c785db7b6d8186e"
integrity sha512-kdR7Fh2YdzsNofJO+DJxLfrlMbW4/NNf78aMXgE21z/tg9GO5W2mKlI1DzsO2JlO5yfZdiYfqb9C9vSLJEx14A==
dependencies:
babel-import-util "^1.1.0"
ember-rfc176-data "^0.3.17"
fs-extra "^9.1.0"
lodash "^4.17.21"
resolve-package-path "^4.0.1"
semver "^7.3.5"
typescript-memoize "^1.0.1"
"@embroider/shared-internals@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-1.0.0.tgz#b081708ac79e4582f17ba0f3e3796e6612a8976c"
@@ -2974,7 +3009,7 @@
"@handlebars/parser" "^1.1.0"
simple-html-tokenizer "^0.5.10"
"@glimmer/tracking@^1.0.2", "@glimmer/tracking@^1.0.4":
"@glimmer/tracking@^1.0.0", "@glimmer/tracking@^1.0.1", "@glimmer/tracking@^1.0.2", "@glimmer/tracking@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@glimmer/tracking/-/tracking-1.0.4.tgz#f1bc1412fe5e2236d0f8d502994a8f88af1bbb21"
integrity sha512-F+oT8I55ba2puSGIzInmVrv/8QA2PcK1VD+GWgFMhF6WC97D+uZX7BFg+a3s/2N4FVBq5KHE+QxZzgazM151Yw==
@@ -7087,7 +7122,7 @@ commander@2.8.x:
dependencies:
graceful-readlink ">= 1.0.0"
commander@7.2.0:
commander@7, commander@7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
@@ -7666,56 +7701,166 @@ ctype@0.5.3:
resolved "https://registry.yarnpkg.com/ctype/-/ctype-0.5.3.tgz#82c18c2461f74114ef16c135224ad0b9144ca12f"
integrity sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=
curved-arrows@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/curved-arrows/-/curved-arrows-0.1.0.tgz#824709316a856e28970c1f577803093a57475bb0"
integrity sha512-FchrDNr8b3ijJOycRM3iT0tc7d5NJSJ5e1fPZibTv73N2yXcgIbSi858R+QfCPRbrjqSWXnR8OaUUu9w+TxTRg==
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.1:
"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.1.tgz#7797eb53ead6b9083c75a45a681e93fc41bc468c"
integrity sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==
dependencies:
internmap "1 - 2"
d3-axis@^3.0.0:
d3-axis@3, d3-axis@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322"
integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
"d3-color@1 - 3":
d3-brush@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
dependencies:
d3-dispatch "1 - 3"
d3-drag "2 - 3"
d3-interpolate "1 - 3"
d3-selection "3"
d3-transition "3"
d3-chord@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966"
integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
dependencies:
d3-path "1 - 3"
"d3-color@1 - 3", d3-color@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a"
integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==
"d3-dispatch@1 - 3":
d3-contour@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-3.0.1.tgz#2c64255d43059599cd0dba8fe4cc3d51ccdd9bbd"
integrity sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==
dependencies:
d3-array "2 - 3"
d3-delaunay@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92"
integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==
dependencies:
delaunator "5"
"d3-dispatch@1 - 3", d3-dispatch@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
"d3-ease@1 - 3":
"d3-drag@2 - 3", d3-drag@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
dependencies:
d3-dispatch "1 - 3"
d3-selection "3"
"d3-dsv@1 - 3", d3-dsv@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
dependencies:
commander "7"
iconv-lite "0.6"
rw "1"
"d3-ease@1 - 3", d3-ease@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
d3-fetch@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22"
integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
dependencies:
d3-dsv "1 - 3"
d3-force@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4"
integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
dependencies:
d3-dispatch "1 - 3"
d3-quadtree "1 - 3"
d3-timer "1 - 3"
"d3-format@1 - 3", d3-format@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.0.1.tgz#e41b81b2ab79277141ec1404aa5d05001da64084"
integrity sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3":
d3-format@3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
d3-geo@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e"
integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==
dependencies:
d3-array "2.5.0 - 3"
d3-hierarchy@3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz#9cbb0ffd2375137a351e6cfeed344a06d4ff4597"
integrity sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA==
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
dependencies:
d3-color "1 - 3"
"d3-path@1 - 3":
"d3-path@1 - 3", d3-path@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e"
integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==
d3-scale@^4.0.2:
d3-polygon@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398"
integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
"d3-quadtree@1 - 3", d3-quadtree@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f"
integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
d3-random@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
d3-scale-chromatic@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a"
integrity sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==
dependencies:
d3-color "1 - 3"
d3-interpolate "1 - 3"
d3-scale@4, d3-scale@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
@@ -7726,11 +7871,18 @@ d3-scale@^4.0.2:
d3-time "2.1.1 - 3"
d3-time-format "2 - 4"
d3-selection@^3.0.0:
"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
d3-shape@3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556"
integrity sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==
dependencies:
d3-path "1 - 3"
d3-shape@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.0.1.tgz#9ccdfb28fd9b0d12f2d8aec234cd5c4a9ea27931"
@@ -7745,19 +7897,26 @@ d3-shape@^3.0.1:
dependencies:
d3-time "1 - 3"
"d3-time@1 - 3", "d3-time@2.1.1 - 3":
d3-time-format@4:
version "4.1.0"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
dependencies:
d3-time "1 - 3"
"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975"
integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==
dependencies:
d3-array "2 - 3"
"d3-timer@1 - 3":
"d3-timer@1 - 3", d3-timer@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
d3-transition@^3.0.1:
"d3-transition@2 - 3", d3-transition@3, d3-transition@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
@@ -7768,6 +7927,53 @@ d3-transition@^3.0.1:
d3-interpolate "1 - 3"
d3-timer "1 - 3"
d3-zoom@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
dependencies:
d3-dispatch "1 - 3"
d3-drag "2 - 3"
d3-interpolate "1 - 3"
d3-selection "2 - 3"
d3-transition "2 - 3"
d3@^7.3.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/d3/-/d3-7.3.0.tgz#f3d5a22c1f658952a6491cf50132f5267ed7a40a"
integrity sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==
dependencies:
d3-array "3"
d3-axis "3"
d3-brush "3"
d3-chord "3"
d3-color "3"
d3-contour "3"
d3-delaunay "6"
d3-dispatch "3"
d3-drag "3"
d3-dsv "3"
d3-ease "3"
d3-fetch "3"
d3-force "3"
d3-format "3"
d3-geo "3"
d3-hierarchy "3"
d3-interpolate "3"
d3-path "3"
d3-polygon "3"
d3-quadtree "3"
d3-random "3"
d3-scale "4"
d3-scale-chromatic "3"
d3-selection "3"
d3-shape "3"
d3-time "3"
d3-time-format "4"
d3-timer "3"
d3-transition "3"
d3-zoom "3"
dag-map@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/dag-map/-/dag-map-2.0.2.tgz#9714b472de82a1843de2fba9b6876938cab44c68"
@@ -7902,6 +8108,13 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
delaunator@5:
version "5.0.0"
resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b"
integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==
dependencies:
robust-predicates "^3.0.0"
delayed-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f"
@@ -8268,7 +8481,7 @@ ember-assign-helper@^0.3.0:
ember-cli-babel "^7.19.0"
ember-cli-htmlbars "^4.3.1"
ember-auto-import@^1.10.1, ember-auto-import@^1.11.3, ember-auto-import@^1.2.19, ember-auto-import@^2.4.0:
ember-auto-import@^1.10.1, ember-auto-import@^1.11.3, ember-auto-import@^1.2.19, ember-auto-import@^1.6.0, ember-auto-import@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/ember-auto-import/-/ember-auto-import-2.4.0.tgz#91c4797f08315728086e35af954cb60bd23c14bc"
integrity sha512-BwF6iTaoSmT2vJ9NEHEGRBCh2+qp+Nlaz/Q7roqNSxl5oL5iMRwenPnHhOoBPTYZvPhcV/KgXR5e+pBQ107plQ==
@@ -8469,7 +8682,7 @@ ember-cli-htmlbars@^3.0.1:
json-stable-stringify "^1.0.1"
strip-bom "^3.0.0"
ember-cli-htmlbars@^4.0.0, ember-cli-htmlbars@^4.3.1:
ember-cli-htmlbars@^4.0.0, ember-cli-htmlbars@^4.2.0, ember-cli-htmlbars@^4.3.1:
version "4.5.0"
resolved "https://registry.yarnpkg.com/ember-cli-htmlbars/-/ember-cli-htmlbars-4.5.0.tgz#d299e4f7eba6f30dc723ee086906cc550beb252e"
integrity sha512-bYJpK1pqFu9AadDAGTw05g2LMNzY8xTCIqQm7dMJmKEoUpLRFbPf4SfHXrktzDh7Q5iggl6Skzf1M0bPlIxARw==
@@ -8935,6 +9148,15 @@ ember-cli@~3.28.5:
workerpool "^6.1.4"
yam "^1.0.0"
ember-click-outside@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ember-click-outside/-/ember-click-outside-3.0.0.tgz#a7271345c5960b5dfe1e45a7f7245d1cf8f383dc"
integrity sha512-X2hLE9Set/tQ9KAEUxfGzCTUgJu/g2sKG+t2ghk/EDz8zF+Y/DPtlxeyZTR6NEPsUbzu3Pqe9gWJUxwaiXC0wg==
dependencies:
ember-cli-babel "^7.26.6"
ember-cli-htmlbars "^5.7.1"
ember-modifier "^2.1.0 || ^3.0.0"
ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.6.tgz#603579ab2fb14be567ef944da3fc2d355f779cd8"
@@ -9189,7 +9411,7 @@ ember-modifier-manager-polyfill@^1.2.0:
ember-cli-version-checker "^2.1.2"
ember-compatibility-helpers "^1.2.0"
ember-modifier@^3.0.0, ember-modifier@^3.1.0:
"ember-modifier@^2.1.0 || ^3.0.0", ember-modifier@^3.0.0, ember-modifier@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-3.1.0.tgz#ba5b0941302accd787ed3dcfc8d20400b77ffc41"
integrity sha512-G5Lj9jVFsD2sVJcRNQfaGKG1p81wT4LGfClBhCuB4TgwP1NGJKdqI+Q8BW2MptONxQt/71UjjUH0YK7Gm9eahg==
@@ -9219,6 +9441,16 @@ ember-named-blocks-polyfill@^0.2.4:
ember-cli-babel "^7.19.0"
ember-cli-version-checker "^5.1.1"
ember-on-resize-modifier@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-on-resize-modifier/-/ember-on-resize-modifier-1.0.0.tgz#b4e12dc023b4d608d7b0f4fa0100722fb860cdd4"
integrity sha512-awmjTV8xIZ0Qc5X+46iYL4NT3f4NahNq+3XJ3Fn7zo8D4QuMzMOM91HqvtFQBWddD8o0v7qZNzW++HXAYAPXkg==
dependencies:
ember-cli-babel "^7.26.6"
ember-cli-htmlbars "^5.7.1"
ember-modifier "^3.0.0"
ember-resize-observer-service "^1.0.0"
ember-overridable-computed@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-overridable-computed/-/ember-overridable-computed-1.0.0.tgz#4fb4a5acc9ec9ed7421586a5a8b4014f5bdb04f7"
@@ -9273,6 +9505,14 @@ ember-render-helpers@^0.2.0:
ember-cli-babel "^7.23.0"
ember-cli-typescript "^4.0.0"
ember-resize-observer-service@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-resize-observer-service/-/ember-resize-observer-service-1.0.0.tgz#6d28e33cf06ddc60e83111069c41796d6739142e"
integrity sha512-KmKNerMJUIJgOjr/AOM+fTa3nhbLLrLLTAw2mZuHhRCF1xEivvvF3ivE9Lst2tVgwi4IgI5Q/4t6AMea/dZpAA==
dependencies:
ember-cli-babel "^7.26.6"
ember-cli-htmlbars "^5.7.1"
ember-resolver@^8.0.3:
version "8.0.3"
resolved "https://registry.yarnpkg.com/ember-resolver/-/ember-resolver-8.0.3.tgz#40f243aa58281bf195c695fe84a6b291e204690a"
@@ -9285,6 +9525,15 @@ ember-resolver@^8.0.3:
ember-cli-version-checker "^5.1.2"
resolve "^1.20.0"
ember-resources@^4.0.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/ember-resources/-/ember-resources-4.4.0.tgz#a3b85a0269c3523ba32a2a5ab15472c8e90a6a41"
integrity sha512-bMk3qdD7sgG33VY3Znri6z2p/XneITuT02zgFROS9flvUE124rnW/m9ND/fySt5EOE8URhS3IZ6MImIgI1dyCg==
dependencies:
"@ember/test-waiters" "^3.0.0"
"@embroider/addon-shim" "^1.2.0"
"@embroider/macros" "^1.2.0"
ember-responsive@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/ember-responsive/-/ember-responsive-4.0.2.tgz#271fb0e619f492493a3332d96332d3684d70cb58"
@@ -9360,6 +9609,30 @@ ember-source@~3.28.8:
semver "^7.3.4"
silent-error "^1.1.1"
ember-stargate@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/ember-stargate/-/ember-stargate-0.4.1.tgz#04ee5595569d77d9f2882636c78aa978b874ac62"
integrity sha512-tw+H5nC29psfIBSfsYwhYZ9BqaRf50baO3isKal3UrEELTAblkjPiA1zuls2VV9+s5cUnqU2ygwW0zKEXNi1zQ==
dependencies:
"@ember/render-modifiers" "^2.0.0"
"@embroider/addon-shim" "^1.0.0"
"@glimmer/component" "^1.0.4"
ember-resources "^4.0.0"
tracked-maps-and-sets "^3.0.1"
ember-statecharts@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/ember-statecharts/-/ember-statecharts-0.13.2.tgz#dbfe00b6a8ba54da8e01af416225d7a0d12e141c"
integrity sha512-4TJCAWIs7vwmZbfH+c9xkXUrMsGho8lcYZQaqgmVEa3A1yzal/ToJHSFqaXm07ckNHkVNoC88Byh4WnbcKKpqA==
dependencies:
"@glimmer/tracking" "^1.0.1"
ember-auto-import "^1.6.0"
ember-cli-babel "^7.22.1"
ember-cli-htmlbars "^5.3.1"
ember-cli-typescript "^3.1.4"
ember-usable "https://github.com/pzuraq/ember-usable#0d03a50"
xstate "^4.12.0"
ember-style-modifier@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/ember-style-modifier/-/ember-style-modifier-0.7.0.tgz#85b3dfd7e4bc2bd546df595f2dab4fb141cf7d87"
@@ -9423,6 +9696,14 @@ ember-text-measurer@^0.6.0:
ember-cli-babel "^7.19.0"
ember-cli-htmlbars "^4.3.1"
ember-tracked-storage-polyfill@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-tracked-storage-polyfill/-/ember-tracked-storage-polyfill-1.0.0.tgz#84d307a1e4badc5f84dca681db2cfea9bdee8a77"
integrity sha512-eL7lZat68E6P/D7b9UoTB5bB5Oh/0aju0Z7PCMi3aTwhaydRaxloE7TGrTRYU+NdJuyNVZXeGyxFxn2frvd3TA==
dependencies:
ember-cli-babel "^7.26.3"
ember-cli-htmlbars "^5.7.1"
"ember-truth-helpers@^2.1.0 || ^3.0.0", ember-truth-helpers@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ember-truth-helpers/-/ember-truth-helpers-3.0.0.tgz#86766bdca4ac9b86bce3d262dff2aabc4a0ea384"
@@ -9430,6 +9711,13 @@ ember-text-measurer@^0.6.0:
dependencies:
ember-cli-babel "^7.22.1"
"ember-usable@https://github.com/pzuraq/ember-usable#0d03a50":
version "0.0.0"
resolved "https://github.com/pzuraq/ember-usable#0d03a500a2f49041a4ddff0bb05b077c3907ed7d"
dependencies:
ember-cli-babel "^7.13.0"
ember-cli-htmlbars "^4.2.0"
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
@@ -11711,6 +11999,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.6:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
icss-utils@^4.0.0, icss-utils@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
@@ -16085,6 +16380,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
robust-predicates@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"
integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==
rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.8.1:
version "2.8.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
@@ -16153,6 +16453,11 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rw@1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
rxjs@^6.4.0:
version "6.6.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
@@ -16201,7 +16506,7 @@ safe-regex@^1.1.0:
dependencies:
ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -17661,6 +17966,16 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
tracked-maps-and-sets@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/tracked-maps-and-sets/-/tracked-maps-and-sets-3.0.2.tgz#6ea1b9f2a367d24f2e9905b74b24437fbce76ea6"
integrity sha512-UIRcWsX1kDOcC/Q2R58weYWlw01EnmWWBwUv3okWS+zMBvsgIfYoO6veHhuNE3hgzWCEImNp46QS5CyKnw5QUA==
dependencies:
"@glimmer/tracking" "^1.0.0"
ember-cli-babel "^7.26.6"
ember-cli-typescript "^4.2.1"
ember-tracked-storage-polyfill "1.0.0"
tree-sync@^1.2.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/tree-sync/-/tree-sync-1.4.0.tgz#314598d13abaf752547d9335b8f95d9a137100d6"
@@ -18711,6 +19026,11 @@ xmlchars@^2.2.0:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
xstate@^4.12.0:
version "4.30.5"
resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.30.5.tgz#c2c6753542578bf15e707b759ebb671f55849410"
integrity sha512-iZhIoZP8Alq4qI8eN/iAYiBRLfLSbSdI29LYsgk2DUvAwMV2J9xGw6CUT2HcH+5Rp5G3XzG6ByRfRmJJ+eEuDQ==
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"