-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI: EventSource project implementation to enable blocking queries for…
… service and node listings (#5267) 1. Add `[]` to relevant computed properties 2. Clean up `index` on the URL 3. Add WithEventSource mixin to auto cleanup EventSources when leaving a view
- Loading branch information
Showing
14 changed files
with
376 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import config from '../config/environment'; | ||
|
||
const enabled = 'CONSUL_UI_DISABLE_REALTIME'; | ||
export function initialize(container) { | ||
if (config[enabled] || window.localStorage.getItem(enabled) !== null) { | ||
return; | ||
} | ||
['node', 'service'] | ||
.map(function(item) { | ||
// create repositories that return a promise resolving to an EventSource | ||
return { | ||
service: `repository/${item}/event-source`, | ||
extend: 'repository/type/event-source', | ||
// Inject our original respository that is used by this class | ||
// within the callable of the EventSource | ||
services: { | ||
content: `repository/${item}`, | ||
}, | ||
}; | ||
}) | ||
.concat([ | ||
// These are the routes where we overwrite the 'default' | ||
// repo service. Default repos are repos that return a promise resovlving to | ||
// an ember-data record or recordset | ||
{ | ||
route: 'dc/nodes/index', | ||
services: { | ||
repo: 'repository/node/event-source', | ||
}, | ||
}, | ||
{ | ||
route: 'dc/services/index', | ||
services: { | ||
repo: 'repository/service/event-source', | ||
}, | ||
}, | ||
]) | ||
.forEach(function(definition) { | ||
if (typeof definition.extend !== 'undefined') { | ||
// Create the class instances that we need | ||
container.register( | ||
`service:${definition.service}`, | ||
container.resolveRegistration(`service:${definition.extend}`).extend({}) | ||
); | ||
} | ||
Object.keys(definition.services).forEach(function(name) { | ||
const servicePath = definition.services[name]; | ||
// inject its dependencies, this could probably detect the type | ||
// but hardcode this for the moment | ||
if (typeof definition.route !== 'undefined') { | ||
container.inject(`route:${definition.route}`, name, `service:${servicePath}`); | ||
} else { | ||
container.inject(`service:${definition.service}`, name, `service:${servicePath}`); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
export default { | ||
initialize, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import Mixin from '@ember/object/mixin'; | ||
|
||
export default Mixin.create({ | ||
reset: function(exiting) { | ||
if (exiting) { | ||
Object.keys(this).forEach(prop => { | ||
if (this[prop] && typeof this[prop].close === 'function') { | ||
this[prop].close(); | ||
// ember doesn't delete on 'resetController' by default | ||
delete this[prop]; | ||
} | ||
}); | ||
} | ||
return this._super(...arguments); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Service from '@ember/service'; | ||
import { get } from '@ember/object'; | ||
|
||
export default Service.extend({ | ||
shouldProxy: function(content, method) { | ||
return false; | ||
}, | ||
init: function() { | ||
this._super(...arguments); | ||
const content = get(this, 'content'); | ||
for (let prop in content) { | ||
if (typeof content[prop] === 'function') { | ||
if (this.shouldProxy(content, prop)) { | ||
this[prop] = function() { | ||
return this.execute(content, prop).then(method => { | ||
return method.apply(this, arguments); | ||
}); | ||
}; | ||
} else if (typeof this[prop] !== 'function') { | ||
this[prop] = function() { | ||
return content[prop](...arguments); | ||
}; | ||
} | ||
} | ||
} | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { inject as service } from '@ember/service'; | ||
import { get } from '@ember/object'; | ||
|
||
import LazyProxyService from 'consul-ui/services/lazy-proxy'; | ||
|
||
import { cache as createCache, BlockingEventSource } from 'consul-ui/utils/dom/event-source'; | ||
|
||
const createProxy = function(repo, find, settings, cache, serialize = JSON.stringify) { | ||
// proxied find*..(id, dc) | ||
const throttle = get(this, 'wait').execute; | ||
return function() { | ||
const key = `${repo.getModelName()}.${find}.${serialize([...arguments])}`; | ||
const _args = arguments; | ||
const newPromisedEventSource = cache; | ||
return newPromisedEventSource( | ||
function(configuration) { | ||
// take a copy of the original arguments | ||
// this means we don't have any configuration object on it | ||
let args = [..._args]; | ||
if (settings.blocking) { | ||
// ...and only add our current cursor/configuration if we are blocking | ||
args = args.concat([configuration]); | ||
} | ||
// save a callback so we can conditionally throttle | ||
const cb = () => { | ||
// original find... with configuration now added | ||
return repo[find](...args) | ||
.then(res => { | ||
if (!settings.blocking) { | ||
// blocking isn't enabled, immediately close | ||
this.close(); | ||
} | ||
return res; | ||
}) | ||
.catch(function(e) { | ||
// setup the aborted connection restarting | ||
// this should happen here to avoid cache deletion | ||
const status = get(e, 'errors.firstObject.status'); | ||
if (status === '0') { | ||
// Any '0' errors (abort) should possibly try again, depending upon the circumstances | ||
} | ||
throw e; | ||
}); | ||
}; | ||
// if we have a cursor (which means its at least the second call) | ||
// and we have a throttle setting, wait for so many ms | ||
if (typeof configuration.cursor !== 'undefined' && settings.throttle) { | ||
return throttle(settings.throttle).then(cb); | ||
} | ||
return cb(); | ||
}, | ||
{ | ||
key: key, | ||
type: BlockingEventSource, | ||
} | ||
); | ||
}; | ||
}; | ||
let cache = null; | ||
export default LazyProxyService.extend({ | ||
store: service('store'), | ||
settings: service('settings'), | ||
wait: service('timeout'), | ||
init: function() { | ||
this._super(...arguments); | ||
if (cache === null) { | ||
cache = createCache({}); | ||
} | ||
}, | ||
willDestroy: function() { | ||
cache = null; | ||
}, | ||
shouldProxy: function(content, method) { | ||
return method.indexOf('find') === 0; | ||
}, | ||
execute: function(repo, find) { | ||
return get(this, 'settings') | ||
.findBySlug('client') | ||
.then(settings => { | ||
return createProxy.bind(this)(repo, find, settings, cache); | ||
}); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
@setupApplicationTest | ||
Feature: dc / list-blocking | ||
In order to see updates without refreshing the page | ||
As a user | ||
I want to see changes if I change consul externally | ||
Background: | ||
Given 1 datacenter model with the value "dc-1" | ||
And settings from yaml | ||
--- | ||
consul:client: | ||
blocking: 1 | ||
throttle: 200 | ||
--- | ||
Scenario: | ||
And 3 [Model] models | ||
And a network latency of 100 | ||
When I visit the [Page] page for yaml | ||
--- | ||
dc: dc-1 | ||
--- | ||
Then the url should be /dc-1/[Url] | ||
And pause until I see 3 [Model] models | ||
And an external edit results in 5 [Model] models | ||
And pause until I see 5 [Model] models | ||
And an external edit results in 1 [Model] model | ||
And pause until I see 1 [Model] model | ||
And an external edit results in 0 [Model] models | ||
And pause until I see 0 [Model] models | ||
Where: | ||
-------------------------------------------- | ||
| Page | Model | Url | | ||
| services | service | services | | ||
| nodes | node | nodes | | ||
-------------------------------------------- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import steps from '../steps'; | ||
|
||
// step definitions that are shared between features should be moved to the | ||
// tests/acceptance/steps/steps.js file | ||
|
||
export default function(assert) { | ||
return steps(assert).then('I should find a file', function() { | ||
assert.ok(true, this.step); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.