mirror of https://github.com/status-im/consul.git
ui: Fix up blocking reconciliation for multiple models (#11237)
> In the future, this should all be moved to each individual repository now, which will mean we can finally get rid of this service. This PR moves reconciliation to 'each individual repository'. I stopped short of getting rid of the service, but its so small now we pretty much don't need it. I'd rather wait until I look at the equivalent DataSink service and see if we can get rid of both equivalent services together (this also currently dependant on work soon to be merged) Reconciliation of models (basically doing the extra work to clean up the ember-data store and bring our frontend 'truth' into line with the actual backend truth) when blocking/long-polling on different views/filters of data is slightly more complicated due to figuring out what should be cleaned up and what should be left in the store. This is especially apparent for KVs. I built in a such a way to hopefully make sure it will all make sense for the future. I also checked that this all worked nicely with all our models, even KV which has never supported blocking queries. I left all that work in so that if we want to enable blocking queries/live updates for KV it now just involves deleting a couple of lines of code. There is a tonne of old stuff that we can clean up here now (our 'fake headers' that we pass around) and I've added that to my list of thing for a 'Big Cleanup PR' that will remove lots of code that we no longer require.
This commit is contained in:
parent
2d05164be1
commit
a9fe39e035
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
ui: Ensure all types of data get reconciled with the backend data
|
||||||
|
```
|
|
@ -28,7 +28,7 @@ export default class KvAdapter extends Adapter {
|
||||||
if (typeof id === 'undefined') {
|
if (typeof id === 'undefined') {
|
||||||
throw new Error('You must specify an id');
|
throw new Error('You must specify an id');
|
||||||
}
|
}
|
||||||
return request`
|
const respond = await request`
|
||||||
GET /v1/kv/${keyToArray(id)}?${{ dc }}
|
GET /v1/kv/${keyToArray(id)}?${{ dc }}
|
||||||
|
|
||||||
${{
|
${{
|
||||||
|
@ -37,6 +37,8 @@ export default class KvAdapter extends Adapter {
|
||||||
index,
|
index,
|
||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
await respond((headers, body) => delete headers['x-consul-index']);
|
||||||
|
return respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should we replace text/plain here with x-www-form-encoded? See
|
// TODO: Should we replace text/plain here with x-www-form-encoded? See
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
{{yield}}
|
{{yield}}
|
||||||
<StateChart @src={{chart}} as |State Guard Action dispatch state|>
|
<StateChart @src={{chart}} as |State Guard Action dispatch state|>
|
||||||
|
|
||||||
<Ref @target={{this}} @name="dispatch" @value={{dispatch}} />
|
<Ref @target={{this}} @name="dispatch" @value={{dispatch}} />
|
||||||
<Guard @name="loaded" @cond={{action "isLoaded"}} />
|
<Guard @name="loaded" @cond={{action "isLoaded"}} />
|
||||||
|
|
||||||
{{did-update (fn dispatch "LOAD") src=src}}
|
|
||||||
|
|
||||||
{{#let (hash
|
{{#let (hash
|
||||||
data=data
|
data=data
|
||||||
|
@ -79,4 +77,5 @@
|
||||||
</State>
|
</State>
|
||||||
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{did-update (fn dispatch "LOAD") src=src}}
|
||||||
</StateChart>
|
</StateChart>
|
|
@ -27,10 +27,6 @@ export default class Outlet extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
setAppRoute(name) {
|
setAppRoute(name) {
|
||||||
const nspace = 'nspace.';
|
|
||||||
if (name.startsWith(nspace)) {
|
|
||||||
name = name.substr(nspace.length);
|
|
||||||
}
|
|
||||||
if (name !== 'loading') {
|
if (name !== 'loading') {
|
||||||
const doc = this.element.ownerDocument.documentElement;
|
const doc = this.element.ownerDocument.documentElement;
|
||||||
if (doc.classList.contains('ember-loading')) {
|
if (doc.classList.contains('ember-loading')) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{did-insert this.connect}}
|
{{did-insert this.connect}}
|
||||||
{{will-destroy this.disconnect}}
|
{{will-destroy this.disconnect}}
|
||||||
{{yield (hash
|
{{yield (hash
|
||||||
model=this.model
|
model=(or this.model this._model)
|
||||||
params=this.params
|
params=this.params
|
||||||
currentName=this.router.currentRoute.name
|
currentName=this.router.currentRoute.name
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,26 @@ export default class RouteComponent extends Component {
|
||||||
@service('routlet') routlet;
|
@service('routlet') routlet;
|
||||||
@service('router') router;
|
@service('router') router;
|
||||||
|
|
||||||
@tracked model;
|
@tracked _model;
|
||||||
|
|
||||||
get params() {
|
get params() {
|
||||||
return this.routlet.paramsFor(this.args.name);
|
return this.routlet.paramsFor(this.args.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get model() {
|
||||||
|
if(this.args.name) {
|
||||||
|
const temp = this.args.name.split('.');
|
||||||
|
temp.pop();
|
||||||
|
const name = temp.join('.');
|
||||||
|
let model = this.routlet.modelFor(name);
|
||||||
|
if(Object.keys(model).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
connect() {
|
connect() {
|
||||||
this.routlet.addRoute(this.args.name, this);
|
this.routlet.addRoute(this.args.name, this);
|
||||||
|
|
|
@ -12,6 +12,9 @@ export default class Kv extends Model {
|
||||||
@attr('string') uid;
|
@attr('string') uid;
|
||||||
@attr('string') Key;
|
@attr('string') Key;
|
||||||
|
|
||||||
|
@attr('number') SyncTime;
|
||||||
|
@attr() meta; // {}
|
||||||
|
|
||||||
@attr('string') Datacenter;
|
@attr('string') Datacenter;
|
||||||
@attr('string') Namespace;
|
@attr('string') Namespace;
|
||||||
@attr('string') Partition;
|
@attr('string') Partition;
|
||||||
|
|
|
@ -1,47 +1,13 @@
|
||||||
import Service, { inject as service } from '@ember/service';
|
import Service, { inject as service } from '@ember/service';
|
||||||
import { get } from '@ember/object';
|
|
||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
import { match } from 'consul-ui/decorators/data-source';
|
import { match } from 'consul-ui/decorators/data-source';
|
||||||
import { singularize } from 'ember-inflector';
|
|
||||||
|
|
||||||
export default class HttpService extends Service {
|
export default class HttpService extends Service {
|
||||||
@service('repository/dc') datacenters;
|
|
||||||
@service('repository/dc') datacenter;
|
|
||||||
@service('repository/kv') kvs;
|
|
||||||
@service('repository/kv') kv;
|
|
||||||
@service('repository/node') leader;
|
|
||||||
@service('repository/service') gateways;
|
|
||||||
@service('repository/service-instance') 'proxy-service-instance';
|
|
||||||
@service('repository/proxy') 'proxy-instance';
|
|
||||||
@service('repository/nspace') namespaces;
|
|
||||||
@service('repository/nspace') namespace;
|
|
||||||
@service('repository/metrics') metrics;
|
|
||||||
@service('repository/oidc-provider') oidc;
|
|
||||||
@service('ui-config') 'ui-config';
|
|
||||||
@service('ui-config') notfound;
|
|
||||||
|
|
||||||
@service('data-source/protocols/http/blocking') type;
|
@service('data-source/protocols/http/blocking') type;
|
||||||
|
|
||||||
source(src, configuration) {
|
source(src, configuration) {
|
||||||
const [, , , , model] = src.split('/');
|
|
||||||
const owner = getOwner(this);
|
|
||||||
const route = match(src);
|
const route = match(src);
|
||||||
const find = route.cb(route.params, owner);
|
const find = route.cb(route.params, getOwner(this));
|
||||||
|
|
||||||
const repo = this[model] || owner.lookup(`service:repository/${singularize(model)}`);
|
|
||||||
if (typeof repo.reconcile === 'function') {
|
|
||||||
configuration.createEvent = function(result = {}, configuration) {
|
|
||||||
const event = {
|
|
||||||
type: 'message',
|
|
||||||
data: result,
|
|
||||||
};
|
|
||||||
const meta = get(event, 'data.meta') || {};
|
|
||||||
if (typeof meta.range === 'undefined') {
|
|
||||||
repo.reconcile(meta);
|
|
||||||
}
|
|
||||||
return event;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return this.type.source(find, configuration);
|
return this.type.source(find, configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ACCESS_READ } from 'consul-ui/abilities/base';
|
||||||
|
|
||||||
export default class RepositoryService extends Service {
|
export default class RepositoryService extends Service {
|
||||||
@service('store') store;
|
@service('store') store;
|
||||||
|
@service('env') env;
|
||||||
@service('repository/permission') permissions;
|
@service('repository/permission') permissions;
|
||||||
|
|
||||||
getModelName() {
|
getModelName() {
|
||||||
|
@ -66,30 +67,33 @@ export default class RepositoryService extends Service {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
reconcile(meta = {}) {
|
shouldReconcile(item, params) {
|
||||||
|
const dc = get(item, 'Datacenter');
|
||||||
|
if (dc !== params.dc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.env.var('CONSUL_NSPACES_ENABLED')) {
|
||||||
|
const nspace = get(item, 'Namespace');
|
||||||
|
if (typeof nspace !== 'undefined' && nspace !== params.ns) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.env.var('CONSUL_PARTITIONS_ENABLED')) {
|
||||||
|
const partition = get(item, 'Partition');
|
||||||
|
if (typeof partiton !== 'undefined' && partition !== params.partition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
reconcile(meta = {}, params = {}, configuration = {}) {
|
||||||
// unload anything older than our current sync date/time
|
// unload anything older than our current sync date/time
|
||||||
if (typeof meta.date !== 'undefined') {
|
if (typeof meta.date !== 'undefined') {
|
||||||
const checkNspace = meta.nspace !== '';
|
|
||||||
const checkPartition = meta.partition !== '';
|
|
||||||
this.store.peekAll(this.getModelName()).forEach(item => {
|
this.store.peekAll(this.getModelName()).forEach(item => {
|
||||||
const dc = get(item, 'Datacenter');
|
const date = get(item, 'SyncTime');
|
||||||
if (dc === meta.dc) {
|
if (!item.isDeleted && typeof date !== 'undefined' && date != meta.date && this.shouldReconcile(item, params)) {
|
||||||
if (checkNspace) {
|
this.store.unloadRecord(item);
|
||||||
const nspace = get(item, 'Namespace');
|
|
||||||
if (typeof nspace !== 'undefined' && nspace !== meta.nspace) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (checkPartition) {
|
|
||||||
const partition = get(item, 'Partition');
|
|
||||||
if (typeof partiton !== 'undefined' && partition !== meta.partition) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const date = get(item, 'SyncTime');
|
|
||||||
if (!item.isDeleted && typeof date !== 'undefined' && date != meta.date) {
|
|
||||||
this.store.unloadRecord(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -107,16 +111,43 @@ export default class RepositoryService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @deprecated
|
// @deprecated
|
||||||
findAllByDatacenter(params, configuration = {}) {
|
async findAllByDatacenter(params, configuration = {}) {
|
||||||
return this.findAll(...arguments);
|
return this.findAll(...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(params = {}, configuration = {}) {
|
async findAll(params = {}, configuration = {}) {
|
||||||
if (typeof configuration.cursor !== 'undefined') {
|
if (typeof configuration.cursor !== 'undefined') {
|
||||||
params.index = configuration.cursor;
|
params.index = configuration.cursor;
|
||||||
params.uri = configuration.uri;
|
params.uri = configuration.uri;
|
||||||
}
|
}
|
||||||
return this.store.query(this.getModelName(), params);
|
return this.query(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async query(params = {}, configuration = {}) {
|
||||||
|
let error, meta, res;
|
||||||
|
try {
|
||||||
|
res = await this.store.query(this.getModelName(), params);
|
||||||
|
meta = res.meta;
|
||||||
|
} catch(e) {
|
||||||
|
switch(get(e, 'errors.firstObject.status')) {
|
||||||
|
case '404':
|
||||||
|
case '403':
|
||||||
|
meta = {
|
||||||
|
date: Number.POSITIVE_INFINITY
|
||||||
|
};
|
||||||
|
error = e;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(typeof meta !== 'undefined') {
|
||||||
|
this.reconcile(meta, params, configuration);
|
||||||
|
}
|
||||||
|
if(typeof error !== 'undefined') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findBySlug(params, configuration = {}) {
|
async findBySlug(params, configuration = {}) {
|
||||||
|
|
|
@ -15,6 +15,10 @@ export default class KvService extends RepositoryService {
|
||||||
return PRIMARY_KEY;
|
return PRIMARY_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldReconcile(item, params) {
|
||||||
|
return super.shouldReconcile(...arguments) && item.Key.startsWith(params.id);
|
||||||
|
}
|
||||||
|
|
||||||
// this one gives you the full object so key,values and meta
|
// this one gives you the full object so key,values and meta
|
||||||
@dataSource('/:partition/:ns/:dc/kv/:id')
|
@dataSource('/:partition/:ns/:dc/kv/:id')
|
||||||
async findBySlug(params, configuration = {}) {
|
async findBySlug(params, configuration = {}) {
|
||||||
|
@ -52,33 +56,17 @@ export default class KvService extends RepositoryService {
|
||||||
// https://www.consul.io/api/kv.html
|
// https://www.consul.io/api/kv.html
|
||||||
@dataSource('/:partition/:ns/:dc/kvs/:id')
|
@dataSource('/:partition/:ns/:dc/kvs/:id')
|
||||||
findAllBySlug(params, configuration = {}) {
|
findAllBySlug(params, configuration = {}) {
|
||||||
|
params.separator = '/';
|
||||||
if (params.id === '/') {
|
if (params.id === '/') {
|
||||||
params.id = '';
|
params.id = '';
|
||||||
}
|
}
|
||||||
return this.authorizeBySlug(
|
return this.authorizeBySlug(
|
||||||
async () => {
|
async () => {
|
||||||
params.separator = '/';
|
let items = await this.findAll(...arguments);
|
||||||
if (typeof configuration.cursor !== 'undefined') {
|
const meta = items.meta;
|
||||||
params.index = configuration.cursor;
|
items = items.filter(item => params.id !== get(item, 'Key'));
|
||||||
}
|
items.meta = meta;
|
||||||
let items;
|
return items;
|
||||||
try {
|
|
||||||
items = await this.store.query(this.getModelName(), params);
|
|
||||||
} catch (e) {
|
|
||||||
if (get(e, 'errors.firstObject.status') === '404') {
|
|
||||||
// TODO: This very much shouldn't be here,
|
|
||||||
// needs to eventually use ember-datas generateId thing
|
|
||||||
// in the meantime at least our fingerprinter
|
|
||||||
// FIXME: Default/token partition
|
|
||||||
const uid = JSON.stringify([params.partition, params.ns, params.dc, params.id]);
|
|
||||||
const record = this.store.peekRecord(this.getModelName(), uid);
|
|
||||||
if (record) {
|
|
||||||
record.unloadRecord();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return items.filter(item => params.id !== get(item, 'Key'));
|
|
||||||
},
|
},
|
||||||
ACCESS_LIST,
|
ACCESS_LIST,
|
||||||
params
|
params
|
||||||
|
|
|
@ -11,17 +11,22 @@ export default class ServiceInstanceService extends RepositoryService {
|
||||||
return modelName;
|
return modelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldReconcile(item, params) {
|
||||||
|
return super.shouldReconcile(...arguments) && item.Service.Service === params.id;
|
||||||
|
}
|
||||||
|
|
||||||
@dataSource('/:partition/:ns/:dc/service-instances/for-service/:id')
|
@dataSource('/:partition/:ns/:dc/service-instances/for-service/:id')
|
||||||
async findByService(params, configuration = {}) {
|
async findByService(params, configuration = {}) {
|
||||||
if (typeof configuration.cursor !== 'undefined') {
|
if (typeof configuration.cursor !== 'undefined') {
|
||||||
params.index = configuration.cursor;
|
params.index = configuration.cursor;
|
||||||
params.uri = configuration.uri;
|
params.uri = configuration.uri;
|
||||||
}
|
}
|
||||||
return this.authorizeBySlug(
|
const instances = await this.authorizeBySlug(
|
||||||
async () => this.store.query(this.getModelName(), params),
|
async () => this.query(params),
|
||||||
ACCESS_READ,
|
ACCESS_READ,
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
@dataSource('/:partition/:ns/:dc/service-instance/:serviceId/:node/:id')
|
@dataSource('/:partition/:ns/:dc/service-instance/:serviceId/:node/:id')
|
||||||
|
|
|
@ -134,7 +134,7 @@ export default class RoutletService extends Service {
|
||||||
const key = pos + 1;
|
const key = pos + 1;
|
||||||
const outlet = outlets.get(keys[key]);
|
const outlet = outlets.get(keys[key]);
|
||||||
if (typeof outlet !== 'undefined') {
|
if (typeof outlet !== 'undefined') {
|
||||||
route.model = outlet.model;
|
route._model = outlet.model;
|
||||||
// TODO: Try to avoid the double computation bug
|
// TODO: Try to avoid the double computation bug
|
||||||
schedule('afterRender', () => {
|
schedule('afterRender', () => {
|
||||||
outlet.routeName = route.args.name;
|
outlet.routeName = route.args.name;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
as |route|>
|
as |route|>
|
||||||
<DataSource
|
<DataSource
|
||||||
@src={{uri '/${partition}/${nspace}/${dc}/kvs/${key}'
|
@src={{uri '/${partition}/${nspace}/${dc}/kv/${key}'
|
||||||
(hash
|
(hash
|
||||||
partition=route.params.partition
|
partition=route.params.partition
|
||||||
nspace=route.params.nspace
|
nspace=route.params.nspace
|
||||||
|
@ -10,11 +10,10 @@ as |route|>
|
||||||
key=(or route.params.key '/')
|
key=(or route.params.key '/')
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@onchange={{action (mut items) value="data"}}
|
@onchange={{action (mut parent) value="data"}}
|
||||||
/>
|
/>
|
||||||
<DataLoader
|
<DataLoader
|
||||||
@src={{
|
@src={{uri '/${partition}/${nspace}/${dc}/kvs/${key}'
|
||||||
uri '/${partition}/${nspace}/${dc}/kv/${key}'
|
|
||||||
(hash
|
(hash
|
||||||
partition=route.params.partition
|
partition=route.params.partition
|
||||||
nspace=route.params.nspace
|
nspace=route.params.nspace
|
||||||
|
@ -29,6 +28,30 @@ as |route|>
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
|
{{#if (eq loader.error.status "404")}}
|
||||||
|
<Notification @sticky={{true}}>
|
||||||
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
|
<strong>Warning!</strong>
|
||||||
|
This KV or parent of this KV was deleted.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{else if (eq loader.error.status "403")}}
|
||||||
|
<Notification @sticky={{true}}>
|
||||||
|
<p data-notification role="alert" class="error notification-update">
|
||||||
|
<strong>Error!</strong>
|
||||||
|
You no longer have access to this KV.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{else}}
|
||||||
|
<Notification @sticky={{true}}>
|
||||||
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
|
<strong>Warning!</strong>
|
||||||
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="loaded">
|
<BlockSlot @name="loaded">
|
||||||
{{#let
|
{{#let
|
||||||
|
@ -45,8 +68,8 @@ as |route|>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parent
|
||||||
loader.data
|
loader.data
|
||||||
items
|
|
||||||
|
|
||||||
as |sort filters parent items|}}
|
as |sort filters parent items|}}
|
||||||
<AppView>
|
<AppView>
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
<Route
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
@title={{item.Node}}
|
|
||||||
as |route|>
|
as |route|>
|
||||||
<DataSource @src={{
|
<DataSource @src={{uri '/${partition}/${nspace}/${dc}/coordinates/for-node/${name}'
|
||||||
uri '/${partition}/${nspace}/${dc}/coordinates/for-node/${name}'
|
|
||||||
(hash
|
(hash
|
||||||
partition=route.params.partition
|
partition=route.params.partition
|
||||||
nspace=route.params.nspace
|
nspace=route.params.nspace
|
||||||
|
@ -21,7 +19,6 @@ as |route|>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
as |loader|>
|
as |loader|>
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
<BlockSlot @name="error">
|
||||||
<AppError
|
<AppError
|
||||||
@error={{loader.error}}
|
@error={{loader.error}}
|
||||||
|
@ -53,7 +50,6 @@ as |route|>
|
||||||
</Notification>
|
</Notification>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="loaded">
|
<BlockSlot @name="loaded">
|
||||||
{{#let
|
{{#let
|
||||||
loader.data
|
loader.data
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { moduleFor, test } from 'ember-qunit';
|
import { moduleFor, test } from 'ember-qunit';
|
||||||
import repo from 'consul-ui/tests/helpers/repo';
|
import repo from 'consul-ui/tests/helpers/repo';
|
||||||
import { env } from '../../../../env';
|
import { env } from '../../../../env';
|
||||||
|
import { get } from '@ember/object';
|
||||||
|
|
||||||
const NAME = 'kv';
|
const NAME = 'kv';
|
||||||
moduleFor(`service:repository/${NAME}`, `Integration | Service | ${NAME}`, {
|
moduleFor(`service:repository/${NAME}`, `Integration | Service | ${NAME}`, {
|
||||||
|
@ -9,11 +10,15 @@ moduleFor(`service:repository/${NAME}`, `Integration | Service | ${NAME}`, {
|
||||||
});
|
});
|
||||||
const dc = 'dc-1';
|
const dc = 'dc-1';
|
||||||
const id = 'key-name';
|
const id = 'key-name';
|
||||||
|
const now = new Date().getTime();
|
||||||
const undefinedNspace = 'default';
|
const undefinedNspace = 'default';
|
||||||
const undefinedPartition = 'default';
|
const undefinedPartition = 'default';
|
||||||
const partition = 'default';
|
const partition = 'default';
|
||||||
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
[undefinedNspace, 'team-1', undefined].forEach(nspace => {
|
||||||
test(`findAllBySlug returns the correct data for list endpoint when nspace is ${nspace}`, function(assert) {
|
test(`findAllBySlug returns the correct data for list endpoint when nspace is ${nspace}`, function(assert) {
|
||||||
|
get(this.subject(), 'store').serializerFor(NAME).timestamp = function() {
|
||||||
|
return now;
|
||||||
|
};
|
||||||
return repo(
|
return repo(
|
||||||
'Kv',
|
'Kv',
|
||||||
'findAllBySlug',
|
'findAllBySlug',
|
||||||
|
@ -43,20 +48,10 @@ const partition = 'default';
|
||||||
const expectedPartition = env('CONSUL_PARTITIONS_ENABLED')
|
const expectedPartition = env('CONSUL_PARTITIONS_ENABLED')
|
||||||
? partition || undefinedPartition
|
? partition || undefinedPartition
|
||||||
: 'default';
|
: 'default';
|
||||||
assert.deepEqual(
|
actual.forEach(item => {
|
||||||
actual,
|
assert.equal(item.uid, `["${expectedPartition}","${expectedNspace}","${dc}","${item.Key}"]`);
|
||||||
expected(function(payload) {
|
assert.equal(item.Datacenter, dc);
|
||||||
return payload.map(item => {
|
});
|
||||||
return {
|
|
||||||
Datacenter: dc,
|
|
||||||
Namespace: expectedNspace,
|
|
||||||
Partition: expectedPartition,
|
|
||||||
uid: `["${expectedPartition}","${expectedNspace}","${dc}","${item}"]`,
|
|
||||||
Key: item,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -81,18 +76,13 @@ const partition = 'default';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(actual, expected) {
|
function(actual, expected) {
|
||||||
assert.deepEqual(
|
expected(
|
||||||
actual,
|
function(payload) {
|
||||||
expected(function(payload) {
|
|
||||||
const item = payload[0];
|
const item = payload[0];
|
||||||
return Object.assign({}, item, {
|
assert.equal(actual.uid, `["${item.Partition || undefinedPartition}","${item.Namespace ||
|
||||||
Datacenter: dc,
|
undefinedNspace}","${dc}","${item.Key}"]`);
|
||||||
Namespace: item.Namespace || undefinedNspace,
|
assert.equal(actual.Datacenter, dc);
|
||||||
Partition: item.Partition || undefinedPartition,
|
}
|
||||||
uid: `["${item.Partition || undefinedPartition}","${item.Namespace ||
|
|
||||||
undefinedNspace}","${dc}","${item.Key}"]`,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue