ui: refactor controllers, routes to use promise hash, comments
This commit is contained in:
parent
cd08e81958
commit
3329cc3846
|
@ -19,6 +19,16 @@
|
|||
{{outlet}}
|
||||
</script>
|
||||
|
||||
<script type="text/x-handlebars" data-template-name="error">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="text-center">
|
||||
uh oh! This is an error page.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-handlebars" data-template-name="dc">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-sm-12 col-xs-12 topbar">
|
||||
|
@ -347,6 +357,7 @@
|
|||
|
||||
<script type="text/x-handlebars" id="index">
|
||||
<div class="col-md-8 col-md-offset-2 vertical-center">
|
||||
{{errorMessage}}
|
||||
{{#each item in model}}
|
||||
{{#link-to 'services' item }}
|
||||
<div class="panel panel-link panel-short">
|
||||
|
|
|
@ -1,83 +1,71 @@
|
|||
// Add mixins
|
||||
App.KvShowController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
||||
|
||||
//
|
||||
// path: /
|
||||
//
|
||||
// The index is for choosing datacenters.
|
||||
//
|
||||
App.IndexController = Ember.Controller.extend({
|
||||
});
|
||||
|
||||
//
|
||||
// path: /:dc
|
||||
//
|
||||
App.DcController = Ember.Controller.extend({
|
||||
// Whether or not the dropdown menu can be seen
|
||||
isDropdownVisible: false,
|
||||
|
||||
checks: function() {
|
||||
var services = this.get('nodes');
|
||||
var nodes = this.get('nodes');
|
||||
var checks = Ember.A()
|
||||
|
||||
// loop over all of the services we have,
|
||||
// merge their checks into one.
|
||||
services.forEach(function(item) {
|
||||
// Combine the checks from all of our nodes
|
||||
// into one.
|
||||
nodes.forEach(function(item) {
|
||||
checks = checks.concat(item.Checks)
|
||||
});
|
||||
|
||||
// return the checks
|
||||
return checks
|
||||
}.property('checks'),
|
||||
}.property('Checks'),
|
||||
|
||||
// Returns the total number of failing checks.
|
||||
//
|
||||
// We treat any non-passing checks as failing
|
||||
//
|
||||
totalChecksFailing: function() {
|
||||
var checks = this.get('checks')
|
||||
return (checks.filterBy('Status', 'critical').get('length') +
|
||||
checks.filterBy('Status', 'warning').get('length'))
|
||||
}.property('Checks'),
|
||||
|
||||
//
|
||||
// Returns the human formatted message for the button state
|
||||
//
|
||||
checkMessage: function() {
|
||||
var checks = this.get('checks')
|
||||
var failingChecks = this.get('totalChecksFailing');
|
||||
var passingChecks = checks.filterBy('Status', 'passing').get('length');
|
||||
|
||||
// return the message for display
|
||||
if (this.get('hasFailingChecks') == true) {
|
||||
return checks.filterBy('Status', 'critical').get('length') + ' checks failing';
|
||||
return failingChecks + ' checks failing';
|
||||
} else {
|
||||
return checks.filterBy('Status', 'passing').get('length') + ' checks passing';
|
||||
return passingChecks + ' checks passing';
|
||||
}
|
||||
|
||||
}.property('checkMessage'),
|
||||
}.property('Checks'),
|
||||
|
||||
//
|
||||
// Boolean if the datacenter has any failing checks.
|
||||
//
|
||||
hasFailingChecks: function() {
|
||||
var checks = this.get('checks')
|
||||
|
||||
// Return a boolean if checks are failing.
|
||||
return (checks.filterBy('Status', 'critical').get('length') > 0);
|
||||
|
||||
}.property('hasFailingChecks'),
|
||||
}.property('Checks'),
|
||||
|
||||
actions: {
|
||||
// Hide and show the dropdown menu
|
||||
toggle: function(item){
|
||||
this.toggleProperty('isDropdownVisible');
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//
|
||||
// path: /:dc/services
|
||||
//
|
||||
// The index is for choosing services.
|
||||
//
|
||||
App.ServicesController = Ember.ArrayController.extend({
|
||||
needs: ['application']
|
||||
});
|
||||
|
||||
//
|
||||
// path: /:dc/services/:name
|
||||
//
|
||||
// An individual service.
|
||||
//
|
||||
App.ServicesShowController = Ember.Controller.extend({
|
||||
needs: ['services']
|
||||
});
|
||||
// Add mixins
|
||||
App.KvShowController = Ember.ObjectController.extend(Ember.Validations.Mixin);
|
||||
|
||||
App.KvShowController.reopen({
|
||||
isLoading: false,
|
||||
|
||||
actions: {
|
||||
// Creates the key from the newKey model
|
||||
// set on the route.
|
||||
createKey: function() {
|
||||
this.set('isLoading', true);
|
||||
|
||||
|
@ -86,21 +74,25 @@ App.KvShowController.reopen({
|
|||
var controller = this;
|
||||
|
||||
// If we don't have a previous model to base
|
||||
// see our parent, or we're not at the root level,
|
||||
// on our parent, or we're not at the root level,
|
||||
// strip the leading slash.
|
||||
if (!topModel || topModel.get('parentKey') != "/") {
|
||||
newKey.set('Key', (topModel.get('parentKey') + newKey.get('Key')));
|
||||
}
|
||||
|
||||
// Put the Key and the Value retrieved from the form
|
||||
Ember.$.ajax({
|
||||
url: "/v1/kv/" + newKey.get('Key'),
|
||||
type: 'PUT',
|
||||
data: newKey.get('Value')
|
||||
}).then(function(response) {
|
||||
controller.set('isLoading', false)
|
||||
// Transition to edit the key
|
||||
controller.transitionToRoute('kv.edit', newKey.get('urlSafeKey'));
|
||||
// Reload the keys in the left column
|
||||
controller.get('keys').reload()
|
||||
}).fail(function(response) {
|
||||
// Render the error message on the form if the request failed
|
||||
controller.set('errorMessage', 'Received error while processing: ' + response.statusText)
|
||||
});
|
||||
|
||||
|
@ -112,19 +104,24 @@ App.KvEditController = Ember.Controller.extend({
|
|||
isLoading: false,
|
||||
|
||||
actions: {
|
||||
// Updates the key set as the model on the route.
|
||||
updateKey: function() {
|
||||
this.set('isLoading', true);
|
||||
|
||||
var key = this.get("model");
|
||||
var controller = this;
|
||||
|
||||
// Put the key and the decoded (plain text) value
|
||||
// from the form.
|
||||
Ember.$.ajax({
|
||||
url: "/v1/kv/" + key.get('Key'),
|
||||
type: 'PUT',
|
||||
data: key.get('valueDecoded')
|
||||
}).then(function(response) {
|
||||
// If success, just reset the loading state.
|
||||
controller.set('isLoading', false)
|
||||
}).fail(function(response) {
|
||||
// Render the error message on the form if the request failed
|
||||
controller.set('errorMessage', 'Received error while processing: ' + response.statusText)
|
||||
})
|
||||
},
|
||||
|
@ -134,15 +131,20 @@ App.KvEditController = Ember.Controller.extend({
|
|||
|
||||
var key = this.get("model");
|
||||
var controller = this;
|
||||
// Get the parent for the transition back up a level
|
||||
// after the delete
|
||||
var parent = key.get('urlSafeParentKey');
|
||||
|
||||
// Delete the key
|
||||
Ember.$.ajax({
|
||||
url: "/v1/kv/" + key.get('Key'),
|
||||
type: 'DELETE'
|
||||
}).then(function(response) {
|
||||
controller.set('isLoading', false);
|
||||
// Tranisiton back up a level
|
||||
controller.transitionToRoute('kv.show', parent);
|
||||
}).fail(function(response) {
|
||||
// Render the error message on the form if the request failed
|
||||
controller.set('errorMessage', 'Received error while processing: ' + response.statusText)
|
||||
})
|
||||
|
||||
|
|
|
@ -6,10 +6,15 @@ App.Service = Ember.Object.extend({
|
|||
// The number of failing checks within the service.
|
||||
//
|
||||
failingChecks: function() {
|
||||
// If the service was returned from `/v1/internal/ui/services`
|
||||
// then we have a aggregated value which we can just grab
|
||||
if (this.get('ChecksCritical') != undefined) {
|
||||
return (this.get('ChecksCritical') + this.get('ChecksWarning'))
|
||||
// Otherwise, we need to filter the child checks by both failing
|
||||
// states
|
||||
} else {
|
||||
return this.get('Checks').filterBy('Status', 'critical').get('length');
|
||||
return (checks.filterBy('Status', 'critical').get('length') +
|
||||
checks.filterBy('Status', 'warning').get('length'))
|
||||
}
|
||||
}.property('Checks'),
|
||||
|
||||
|
@ -17,8 +22,12 @@ App.Service = Ember.Object.extend({
|
|||
// The number of passing checks within the service.
|
||||
//
|
||||
passingChecks: function() {
|
||||
// If the service was returned from `/v1/internal/ui/services`
|
||||
// then we have a aggregated value which we can just grab
|
||||
if (this.get('ChecksPassing') != undefined) {
|
||||
return this.get('ChecksPassing')
|
||||
// Otherwise, we need to filter the child checks by both failing
|
||||
// states
|
||||
} else {
|
||||
return this.get('Checks').filterBy('Status', 'passing').get('length');
|
||||
}
|
||||
|
@ -53,7 +62,10 @@ App.Node = Ember.Object.extend({
|
|||
// The number of failing checks within the service.
|
||||
//
|
||||
failingChecks: function() {
|
||||
return this.get('Checks').filterBy('Status', 'critical').get('length');
|
||||
var checks = this.get('Checks');
|
||||
// We view both warning and critical as failing
|
||||
return (checks.filterBy('Status', 'critical').get('length') +
|
||||
checks.filterBy('Status', 'warning').get('length'))
|
||||
}.property('Checks'),
|
||||
|
||||
//
|
||||
|
@ -89,33 +101,46 @@ App.Node = Ember.Object.extend({
|
|||
// A key/value object
|
||||
//
|
||||
App.Key = Ember.Object.extend(Ember.Validations.Mixin, {
|
||||
// Validates using the Ember.Valdiations library
|
||||
validations: {
|
||||
Key: { presence: true },
|
||||
Value: { presence: true }
|
||||
},
|
||||
|
||||
// Boolean if the key is valid
|
||||
keyValid: Ember.computed.empty('errors.Key'),
|
||||
// Boolean if the value is valid
|
||||
valueValid: Ember.computed.empty('errors.Value'),
|
||||
|
||||
// The key with the parent removed.
|
||||
// This is only for display purposes, and used for
|
||||
// showing the key name inside of a nested key.
|
||||
keyWithoutParent: function() {
|
||||
return (this.get('Key').replace(this.get('parentKey'), ''));
|
||||
}.property('Key'),
|
||||
|
||||
// Boolean if the key is a "folder" or not, i.e is a nested key
|
||||
// that feels like a folder. Used for UI
|
||||
isFolder: function() {
|
||||
return (this.get('Key').slice(-1) == "/")
|
||||
}.property('Key'),
|
||||
|
||||
// The dasherized URL safe version of the key for routing
|
||||
urlSafeKey: function() {
|
||||
return this.get('Key').replace(/\//g, "-")
|
||||
}.property('Key'),
|
||||
|
||||
// The dasherized URL safe version of the parent key for routing
|
||||
urlSafeParentKey: function() {
|
||||
return this.get('parentKey').replace(/\//g, "-")
|
||||
}.property('Key'),
|
||||
|
||||
// Determines what route to link to. If it's a folder,
|
||||
// it will link to kv.show. Otherwise, kv.edit
|
||||
linkToRoute: function() {
|
||||
var key = this.get('urlSafeKey')
|
||||
|
||||
// If the key ends in - it's a folder
|
||||
if (key.slice(-1) === "-") {
|
||||
return 'kv.show'
|
||||
} else {
|
||||
|
@ -123,39 +148,62 @@ App.Key = Ember.Object.extend(Ember.Validations.Mixin, {
|
|||
}
|
||||
}.property('Key'),
|
||||
|
||||
// The base64 decoded value of the key.
|
||||
// if you set on this key, it will update
|
||||
// the key.Value
|
||||
valueDecoded: function(key, value) {
|
||||
// Setter
|
||||
|
||||
// setter
|
||||
if (arguments.length > 1) {
|
||||
this.set('Value', window.btoa(value));
|
||||
this.set('Value', value);
|
||||
}
|
||||
|
||||
// getter
|
||||
|
||||
// If the value is null, we don't
|
||||
// want to try and base64 decode it, so just return
|
||||
if (this.get('Value') === null) {
|
||||
return "";
|
||||
}
|
||||
// getter
|
||||
|
||||
// base64 decode the value
|
||||
return window.atob(this.get('Value'));
|
||||
}.property('Value'),
|
||||
|
||||
|
||||
// An array of the key broken up by the /
|
||||
keyParts: function() {
|
||||
var key = this.get('Key');
|
||||
|
||||
// If the key is a folder, remove the last
|
||||
// slash to split properly
|
||||
if (key.slice(-1) == "/") {
|
||||
key = key.substring(0, key.length - 1);
|
||||
}
|
||||
|
||||
return key.split('/');
|
||||
}.property('Key'),
|
||||
|
||||
// The parent Key is the key one level above this.Key
|
||||
// key: baz/bar/foobar/
|
||||
// grandParent: baz/bar/
|
||||
parentKey: function() {
|
||||
var parts = this.get('keyParts').toArray();
|
||||
|
||||
// Remove the last item, essentially going up a level
|
||||
// in hiearchy
|
||||
parts.pop();
|
||||
|
||||
return parts.join("/") + "/";
|
||||
}.property('Key'),
|
||||
|
||||
// The grandParent Key is the key two levels above this.Key
|
||||
// key: baz/bar/foobar/
|
||||
// grandParent: baz/
|
||||
grandParentKey: function() {
|
||||
var parts = this.get('keyParts').toArray();
|
||||
|
||||
// Remove the last two items, jumping two levels back
|
||||
parts.pop();
|
||||
parts.pop();
|
||||
|
||||
|
|
|
@ -1,25 +1,39 @@
|
|||
window.App = Ember.Application.create({
|
||||
rootElement: "#app",
|
||||
LOG_TRANSITIONS: true,
|
||||
// The baseUrl for AJAX requests
|
||||
// TODO implement in AJAX logic
|
||||
baseUrl: 'http://localhost:8500'
|
||||
});
|
||||
|
||||
|
||||
App.Router.map(function() {
|
||||
// Our parent datacenter resource sets the namespace
|
||||
// for the entire application
|
||||
this.resource("dc", {path: "/:dc"}, function() {
|
||||
// Services represent a consul service
|
||||
this.resource("services", { path: "/services" }, function(){
|
||||
// Show an individual service
|
||||
this.route("show", { path: "/:name" });
|
||||
});
|
||||
// Nodes represent a consul node
|
||||
this.resource("nodes", { path: "/nodes" }, function() {
|
||||
// Show an individual node
|
||||
this.route("show", { path: "/:name" });
|
||||
});
|
||||
// Key/Value
|
||||
this.resource("kv", { path: "/kv" }, function(){
|
||||
// This route just redirects to /-
|
||||
this.route("index", { path: "/" });
|
||||
// List keys. This is more like an index
|
||||
this.route("show", { path: "/:key" });
|
||||
// Edit a specific key
|
||||
this.route("edit", { path: "/:key/edit" });
|
||||
})
|
||||
});
|
||||
|
||||
// Shows a datacenter picker. If you only have one
|
||||
// it just redirects you through.
|
||||
this.route("index", { path: "/" });
|
||||
});
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
//
|
||||
App.BaseRoute = Ember.Route.extend({
|
||||
actions: {
|
||||
// Used to link to keys that are not objects,
|
||||
// like parents and grandParents
|
||||
linkToKey: function(key) {
|
||||
key = key.replace(/\//g, "-")
|
||||
|
||||
|
@ -20,37 +22,37 @@ App.BaseRoute = Ember.Route.extend({
|
|||
//
|
||||
// The route for choosing datacenters, typically the first route loaded.
|
||||
//
|
||||
// Note: This *does not* extend from BaseRoute as that could cause
|
||||
// and loop of transitions.
|
||||
//
|
||||
App.IndexRoute = Ember.Route.extend({
|
||||
App.IndexRoute = App.BaseRoute.extend({
|
||||
// Retrieve the list of datacenters
|
||||
model: function(params) {
|
||||
return Ember.$.getJSON('/v1/catalog/datacenters').then(function(data) {
|
||||
return data
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('content', model);
|
||||
})
|
||||
},
|
||||
|
||||
afterModel: function(model, transition) {
|
||||
// If we only have one datacenter, jump
|
||||
// straight to it and bypass the global
|
||||
// view
|
||||
if (model.get('length') === 1) {
|
||||
this.transitionTo('services', model[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// The base DC route
|
||||
|
||||
// The parent route for all resources. This keeps the top bar
|
||||
// functioning, as well as the per-dc requests.
|
||||
App.DcRoute = App.BaseRoute.extend({
|
||||
model: function(params) {
|
||||
// Return a promise hash to retreieve the
|
||||
// dcs and nodes used in the header
|
||||
return Ember.RSVP.hash({
|
||||
dc: params.dc,
|
||||
dcs: Ember.$.getJSON('/v1/catalog/datacenters'),
|
||||
nodes: Ember.$.getJSON('/v1/internal/ui/nodes').then(function(data) {
|
||||
objs = [];
|
||||
|
||||
// Merge the nodes into a list and create objects out of them
|
||||
data.map(function(obj){
|
||||
objs.push(App.Node.create(obj));
|
||||
});
|
||||
|
@ -69,6 +71,7 @@ App.DcRoute = App.BaseRoute.extend({
|
|||
|
||||
|
||||
App.KvIndexRoute = App.BaseRoute.extend({
|
||||
// If they hit /kv we want to just move them to /kv/-
|
||||
beforeModel: function() {
|
||||
this.transitionTo('kv.show', '-')
|
||||
}
|
||||
|
@ -76,16 +79,15 @@ App.KvIndexRoute = App.BaseRoute.extend({
|
|||
|
||||
App.KvShowRoute = App.BaseRoute.extend({
|
||||
model: function(params) {
|
||||
// Convert the key back to the format consul understands
|
||||
var key = params.key.replace(/-/g, "/")
|
||||
|
||||
// Return a promise to retrieve the ?keys for that namespace
|
||||
return Ember.$.getJSON('/v1/kv/' + key + '?keys&seperator=' + '/').then(function(data) {
|
||||
|
||||
objs = [];
|
||||
|
||||
data.map(function(obj){
|
||||
objs.push(App.Key.create({Key: obj}));
|
||||
});
|
||||
|
||||
return objs;
|
||||
});
|
||||
},
|
||||
|
@ -108,15 +110,20 @@ App.KvEditRoute = App.BaseRoute.extend({
|
|||
key = key.substring(0, key.length - 1);
|
||||
}
|
||||
parts = key.split('/');
|
||||
// Go one level up
|
||||
parts.pop();
|
||||
// If we are all the way up, just return nothing for the root
|
||||
if (parts.length == 0) {
|
||||
parentKey = ""
|
||||
} else {
|
||||
// Add a slash
|
||||
parentKey = parts.join("/") + "/";
|
||||
}
|
||||
|
||||
// Return a promise hash to get the data for both columns
|
||||
return Ember.RSVP.hash({
|
||||
key: Ember.$.getJSON('/v1/kv/' + keyName).then(function(data) {
|
||||
// Convert the returned data to a Key
|
||||
return App.Key.create().setProperties(data[0]);
|
||||
}),
|
||||
keys: keysPromise = Ember.$.getJSON('/v1/kv/' + parentKey + '?keys&seperator=' + '/').then(function(data) {
|
||||
|
@ -132,6 +139,9 @@ App.KvEditRoute = App.BaseRoute.extend({
|
|||
setupController: function(controller, models) {
|
||||
controller.set('content', models.key);
|
||||
|
||||
// If we don't have the cached model from our
|
||||
// the kv.show controller, we need to go get it,
|
||||
// otherwise we just load what we have.
|
||||
if (this.modelFor('kv.show') == undefined ) {
|
||||
controller.set('siblings', models.keys);
|
||||
} else {
|
||||
|
@ -140,13 +150,9 @@ App.KvEditRoute = App.BaseRoute.extend({
|
|||
}
|
||||
});
|
||||
|
||||
/// services
|
||||
|
||||
//
|
||||
// Display all the services, allow to drill down into the specific services.
|
||||
//
|
||||
App.ServicesRoute = App.BaseRoute.extend({
|
||||
model: function(params) {
|
||||
// Return a promise to retrieve all of the services
|
||||
return Ember.$.getJSON('/v1/internal/ui/services').then(function(data) {
|
||||
objs = [];
|
||||
data.map(function(obj){
|
||||
|
@ -155,56 +161,29 @@ App.ServicesRoute = App.BaseRoute.extend({
|
|||
return objs
|
||||
});
|
||||
},
|
||||
//
|
||||
// Set the services as the routes default model to be called in
|
||||
// the template as {{model}}
|
||||
//
|
||||
setupController: function(controller, model) {
|
||||
//
|
||||
// Since we have 2 column layout, we need to also display the
|
||||
// list of services on the left. Hence setting the attribute
|
||||
// {{services}} on the controller.
|
||||
//
|
||||
controller.set('services', model);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
// Display an individual service, as well as the global services in the left
|
||||
// column.
|
||||
//
|
||||
App.ServicesShowRoute = App.BaseRoute.extend({
|
||||
//
|
||||
// Set the model on the route. We look up the specific service
|
||||
// by it's identifier passed via the route
|
||||
//
|
||||
model: function(params) {
|
||||
// Here we just use the built-in health endpoint, as it gives us everything
|
||||
// we need.
|
||||
return Ember.$.getJSON('/v1/health/service/' + params.name).then(function(data) {
|
||||
objs = [];
|
||||
|
||||
data.map(function(obj){
|
||||
objs.push(App.Node.create(obj));
|
||||
});
|
||||
|
||||
return objs;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/// nodes
|
||||
|
||||
//
|
||||
// Display an individual node, as well as the global nodes in the left
|
||||
// column.
|
||||
//
|
||||
App.NodesShowRoute = App.BaseRoute.extend({
|
||||
//
|
||||
// Set the model on the route. We look up the specific node
|
||||
// by it's identifier passed via the route
|
||||
//
|
||||
model: function(params) {
|
||||
// Return a promise hash of the node and nodes
|
||||
return Ember.RSVP.hash({
|
||||
node: Ember.$.getJSON('/v1/internal/ui/node/' + params.name).then(function(data) {
|
||||
return App.Node.create(data)
|
||||
|
@ -226,12 +205,9 @@ App.NodesShowRoute = App.BaseRoute.extend({
|
|||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Display all the nodes, allow to drill down into the specific nodes.
|
||||
//
|
||||
App.NodesRoute = App.BaseRoute.extend({
|
||||
|
||||
model: function(params) {
|
||||
// Return a promise containing the nodes
|
||||
return Ember.$.getJSON('/v1/internal/ui/nodes').then(function(data) {
|
||||
objs = [];
|
||||
data.map(function(obj){
|
||||
|
@ -240,16 +216,7 @@ App.NodesRoute = App.BaseRoute.extend({
|
|||
return objs
|
||||
});
|
||||
},
|
||||
//
|
||||
// Set the node as the routes default model to be called in
|
||||
// the template as {{model}}. This is the "expanded" view.
|
||||
//
|
||||
setupController: function(controller, model) {
|
||||
//
|
||||
// Since we have 2 column layout, we need to also display the
|
||||
// list of nodes on the left. Hence setting the attribute
|
||||
// {{nodes}} on the controller.
|
||||
//
|
||||
controller.set('nodes', model);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue