diff --git a/admin/js/config.js b/admin/js/config.js
index 53376f3..e624f11 100644
--- a/admin/js/config.js
+++ b/admin/js/config.js
@@ -35,6 +35,8 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Cons
.listView()
.title('Nodes')
.perPage(30)
+ .sortDir('ASC')
+ .sortField('hostname')
.actions([])
.batchActions([])
.exportFields([])
@@ -52,13 +54,17 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Cons
? ''
: '';
}),
- nga.field('monitoring').cssClasses(nodeClasses).template(function (node) {
- if (!node.values.monitoring) {
- return '';
+ nga.field('monitoringState').cssClasses(nodeClasses).template(function (node) {
+ switch (node.values.monitoringState) {
+ case 'active':
+ return '';
+
+ case 'pending':
+ return '';
+
+ default:
+ return '';
}
- return node.values.monitoringConfirmed
- ? ''
- : '';
})
])
.listActions([
@@ -104,6 +110,8 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Cons
.listView()
.title('Background-Jobs')
.perPage(30)
+ .sortDir('ASC')
+ .sortField('id')
.actions([])
.batchActions([])
.exportFields([])
diff --git a/server/resources/nodeResource.js b/server/resources/nodeResource.js
index 8215471..c30d27d 100644
--- a/server/resources/nodeResource.js
+++ b/server/resources/nodeResource.js
@@ -100,15 +100,22 @@ angular.module('ffffng').factory('NodeResource', function (
return Resources.error(res, err);
}
- // TODO: Sort + Filter
+ // TODO: Filter
- return NodeService.getAllNodes(restParams._page, restParams._perPage, function (err, nodes, total) {
+ return NodeService.getAllNodes(function (err, nodes, total) {
if (err) {
return Resources.error(res, err);
}
+ var sortedNodes = Resources.sort(
+ nodes,
+ ['token', 'mac', 'hostname', 'key', 'coords', 'monitoringState'],
+ restParams
+ );
+ var pageNodes = Resources.getPageEntities(sortedNodes, restParams);
+
res.set('X-Total-Count', total);
- return Resources.success(res, nodes);
+ return Resources.success(res, pageNodes);
});
});
}
diff --git a/server/resources/taskResource.js b/server/resources/taskResource.js
index b2ef6bd..db09100 100644
--- a/server/resources/taskResource.js
+++ b/server/resources/taskResource.js
@@ -79,15 +79,14 @@ angular.module('ffffng').factory('TaskResource', function (
return Resources.error(res, err);
}
- // TODO: Sort
-
- var tasks = _.values(Scheduler.getTasks());
+ var tasks = Resources.sort(
+ _.values(Scheduler.getTasks()),
+ ['id', 'name', 'schedule', 'state', 'runningSince', 'lastRunStarted'],
+ restParams
+ );
var total = tasks.length;
- var page = restParams._page;
- var perPage = restParams._perPage;
-
- var pageTasks = tasks.slice((page - 1) * perPage, page * perPage);
+ var pageTasks = Resources.getPageEntities(tasks, restParams);
res.set('X-Total-Count', total);
return Resources.success(res, _.map(pageTasks, toExternalTask));
diff --git a/server/services/nodeService.js b/server/services/nodeService.js
index fd53c83..7c4300b 100644
--- a/server/services/nodeService.js
+++ b/server/services/nodeService.js
@@ -217,6 +217,7 @@ angular.module('ffffng')
var pending = value === 'pending';
node.monitoring = active || pending;
node.monitoringConfirmed = active;
+ node.monitoringState = active ? 'active' : (pending ? 'pending' : '');
} else if (key === 'monitoringToken') {
nodeSecrets.monitoringToken = value;
} else {
@@ -369,13 +370,12 @@ angular.module('ffffng')
deleteNodeFile(token, callback);
},
- getAllNodes: function (page, perPage, callback) {
- var files = _.sortBy(findNodeFiles({}));
+ getAllNodes: function (callback) {
+ var files = findNodeFiles({});
var total = files.length;
- var pageFiles = files.slice((page - 1) * perPage, page * perPage);
async.mapLimit(
- pageFiles,
+ files,
MAX_PARALLEL_NODES_PARSING,
parseNodeFile,
function (err, nodes) {
diff --git a/server/utils/resources.js b/server/utils/resources.js
index fb634d2..2f09730 100644
--- a/server/utils/resources.js
+++ b/server/utils/resources.js
@@ -36,6 +36,24 @@ angular.module('ffffng').factory('Resources', function (_, Constraints, Validato
callback(null, restParams);
},
+ sort: function (entities, allowedSortFields, restParams) {
+ var sortField = _.indexOf(allowedSortFields, restParams._sortField) >= 0 ? restParams._sortField : undefined;
+ if (!sortField) {
+ return entities;
+ }
+
+ var sorted = _.sortBy(entities, [sortField]);
+
+ return restParams._sortDir === 'ASC' ? sorted : _.reverse(sorted);
+ },
+
+ getPageEntities: function (entities, restParams) {
+ var page = restParams._page;
+ var perPage = restParams._perPage;
+
+ return entities.slice((page - 1) * perPage, page * perPage);
+ },
+
success: function (res, data) {
respond(res, 200, data);
},
diff --git a/server/validation/validator.js b/server/validation/validator.js
index d597d45..92ce7a8 100644
--- a/server/validation/validator.js
+++ b/server/validation/validator.js
@@ -1,8 +1,10 @@
'use strict';
angular.module('ffffng').factory('Validator', function (_, Strings, Logger) {
+ // TODO: sanitize input for further processing as specified by constraints (correct types, trimming, etc.)
+
function isValidBoolean(value) {
- return _.isBoolean(value);
+ return _.isBoolean(value) || value === 'true' || value === 'false';
}
function isValidNumber(constraint, value) {
@@ -29,6 +31,14 @@ angular.module('ffffng').factory('Validator', function (_, Strings, Logger) {
return true;
}
+ function isValidEnum(constraint, value) {
+ if (!_.isString(value)) {
+ return false;
+ }
+
+ return _.indexOf(constraint.allowed, value) >= 0;
+ }
+
function isValidString(constraint, value) {
if (!_.isString(value)) {
return false;
@@ -50,6 +60,9 @@ angular.module('ffffng').factory('Validator', function (_, Strings, Logger) {
case 'number':
return isValidNumber(constraint, value);
+ case 'enum':
+ return isValidEnum(constraint, value);
+
case 'string':
return isValidString(constraint, value);
}
diff --git a/shared/validation/constraints.js b/shared/validation/constraints.js
index 4c8be92..20e864c 100644
--- a/shared/validation/constraints.js
+++ b/shared/validation/constraints.js
@@ -61,6 +61,17 @@ angular.module('ffffng').constant('Constraints', {
max: 50,
optional: true,
default: 20
+ },
+ _sortDir: {
+ type: 'enum',
+ allowed: ['ASC', 'DESC'],
+ optional: true,
+ default: 'ASC'
+ },
+ _sortField: {
+ type: 'string',
+ regex: /^[a-zA-Z0-9_]{1,32}$/,
+ optional: true
}
}
}