GUAC-586: Invoke REST service functions across multiple data sources using dataSourceService.apply().

This commit is contained in:
Michael Jumper
2015-08-31 14:08:11 -07:00
parent 8f39671c6b
commit 7235ed980f
6 changed files with 138 additions and 179 deletions

View File

@@ -39,6 +39,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
var $q = $injector.get('$q');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var dataSourceService = $injector.get('dataSourceService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
var schemaService = $injector.get('schemaService');
@@ -326,63 +327,8 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
$scope.attributes = attributes;
});
/**
* Retrieves all user objects having the given username from each of the
* given data sources, returning a promise which resolves to a map of those
* users by data source identifier. If any data source returns an error,
* that data source is omitted from the results.
*
* @param {String[]} dataSources
* The identifiers of the data sources to query.
*
* @param {String} username
* The username of the user to retrieve from each of the given data
* sources.
*
* @returns {Promise.<Object.<String, User>>}
* A promise which resolves to a map of user objects by data source
* identifier.
*/
var getUserAccounts = function getUserAccounts(dataSources, username) {
var deferred = $q.defer();
var userRequests = [];
var accounts = {};
// Retrieve the requested user account from all data sources
angular.forEach(dataSources, function retrieveUser(dataSource) {
// Add promise to list of pending requests
var deferredUserRequest = $q.defer();
userRequests.push(deferredUserRequest.promise);
// Retrieve user from data source
userService.getUser(dataSource, username)
.success(function userRetrieved(user) {
accounts[dataSource] = user;
deferredUserRequest.resolve();
})
// Ignore any errors
.error(function userRetrievalFailed() {
deferredUserRequest.resolve();
});
});
// Resolve when all requests are completed
$q.all(userRequests)
.then(function accountsRetrieved() {
deferred.resolve(accounts);
});
return deferred.promise;
};
// Pull user data
getUserAccounts(dataSources, username)
dataSourceService.apply(userService.getUser, dataSources, username)
.then(function usersReceived(users) {
// Get user for currently-selected data source
@@ -427,7 +373,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
});
// Retrieve all connections for which we have ADMINISTER permission
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER])
connectionGroupService.getConnectionGroupTree(dataSource, ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER])
.success(function connectionGroupReceived(rootGroup) {
$scope.rootGroup = rootGroup;
});

View File

@@ -34,8 +34,9 @@ angular.module('navigation').factory('userPageService', ['$injector',
// Get required services
var $q = $injector.get('$q');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get("connectionGroupService");
var permissionService = $injector.get("permissionService");
var connectionGroupService = $injector.get('connectionGroupService');
var dataSourceService = $injector.get('dataSourceService');
var permissionService = $injector.get('permissionService');
var service = {};
@@ -259,7 +260,8 @@ angular.module('navigation').factory('userPageService', ['$injector',
var deferred = $q.defer();
// Retrieve current permissions
permissionService.getAllPermissions(
dataSourceService.apply(
permissionService.getPermissions,
authenticationService.getAvailableDataSources(),
authenticationService.getCurrentUsername()
)
@@ -346,7 +348,8 @@ angular.module('navigation').factory('userPageService', ['$injector',
});
// Retrieve current permissions
permissionService.getAllPermissions(
dataSourceService.apply(
permissionService.getPermissions,
authenticationService.getAvailableDataSources(),
authenticationService.getCurrentUsername()
)

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Service which contains all REST API response caches.
*/
angular.module('rest').factory('dataSourceService', ['$injector',
function dataSourceService($injector) {
// Required services
var $q = $injector.get('$q');
// Service containing all caches
var service = {};
/**
* Invokes the given function once for each of the given data sources,
* passing that data source as the first argument to each invocation,
* followed by any additional arguments passed to apply(). The results of
* each invocation are aggregated into a map by data source identifier,
* and handled through a single promise which is resolved or rejected
* depending on the success/failure of each resulting REST call. Any error
* results in rejection of the entire apply() operation, except 404 ("NOT
* FOUND") errors, which are ignored.
*
* @param {Function} fn
* The function to call for each of the given data sources. The data
* source identifier will be given as the first argument, followed by
* the rest of the arguments given to apply(), in order. The function
* must return a Promise which is resolved or rejected depending on the
* result of the REST call.
*
* @param {String[]} dataSources
* The array or data source identifiers against which the given
* function should be called.
*
* @param {...*} args
* Any additional arguments to pass to the given function each time it
* is called.
*
* @returns {Promise.<Object.<String, *>>}
* A Promise which resolves with a map of data source identifier to
* corresponding result. The result will be the exact object or value
* provided as the resolution to the Promise returned by calls to the
* given function.
*/
service.apply = function apply(fn, dataSources) {
var deferred = $q.defer();
var requests = [];
var results = {};
// Build array of arguments to pass to the given function
var args = [];
for (var i = 2; i < arguments.length; i++)
args.push(arguments[i]);
// Retrieve the root group from all data sources
angular.forEach(dataSources, function invokeAgainstDataSource(dataSource) {
// Add promise to list of pending requests
var deferredRequest = $q.defer();
requests.push(deferredRequest.promise);
// Retrieve root group from data source
fn.apply(this, [dataSource].concat(args))
// Store result on success
.then(function immediateRequestSucceeded(response) {
results[dataSource] = response.data;
deferredRequest.resolve();
},
// Fail on any errors (except "NOT FOUND")
function immediateRequestFailed(response) {
// Ignore "NOT FOUND" errors
if (response.status === 404)
deferredRequest.resolve();
// Explicitly abort for all other errors
else
deferredRequest.reject(response);
});
});
// Resolve if all requests succeed
$q.all(requests).then(function requestsSucceeded() {
deferred.resolve(results);
},
// Reject if at least one request fails
function requestFailed(response) {
deferred.reject(response);
});
return deferred.promise;
};
return service;
}]);

View File

@@ -71,63 +71,6 @@ angular.module('rest').factory('permissionService', ['$injector',
};
/**
* Returns a promise which resolves with all permissions available to the
* given user, as a map of all PermissionSet objects by the identifier of
* their corresponding data source. All given data sources are queried. If
* an error occurs while retrieving any PermissionSet, the promise will be
* rejected.
*
* @param {String[]} dataSources
* The unique identifier of the data sources containing the user whose
* permissions should be retrieved. These identifiers corresponds to
* AuthenticationProviders within the Guacamole web application.
*
* @param {String} username
* The username of the user to retrieve the permissions for.
*
* @returns {Promise.<Object.<String, PermissionSet>>}
* A promise which resolves with all permissions available to the
* current user, as a map of app PermissionSet objects by the
* identifier of their corresponding data source.
*/
service.getAllPermissions = function getAllPermissions(dataSources, username) {
var deferred = $q.defer();
var permissionSetRequests = [];
var permissionSets = {};
// Retrieve all permissions from all data sources
angular.forEach(dataSources, function retrievePermissions(dataSource) {
permissionSetRequests.push(
service.getPermissions(dataSource, username)
.success(function permissionsRetrieved(permissions) {
permissionSets[dataSource] = permissions;
})
);
});
// Resolve when all requests are completed
$q.all(permissionSetRequests)
.then(
// All requests completed successfully
function allPermissionsRetrieved() {
deferred.resolve(permissionSets);
},
// At least one request failed
function permissionRetrievalFailed(e) {
deferred.reject(e);
}
);
return deferred.promise;
};
/**
* Makes a request to the REST API to add permissions for a given user,
* returning a promise that can be used for processing the results of the

View File

@@ -78,65 +78,6 @@ angular.module('rest').factory('userService', ['$injector',
};
/**
* Returns a promise which resolves with all users accessible by the
* current user, as a map of @link{User} arrays by the identifier of their
* corresponding data source. All given data sources are queried. If an
* error occurs while retrieving any user, the promise will be rejected.
*
* @param {String[]} dataSources
* The unique identifier of the data sources containing the user to be
* retrieved. These identifiers correspond to AuthenticationProviders
* within the Guacamole web application.
*
* @param {String[]} [permissionTypes]
* The set of permissions to filter with. A user must have one or more
* of these permissions for a user to appear in the result.
* If null, no filtering will be performed. Valid values are listed
* within PermissionSet.ObjectType.
*
* @returns {Promise.<Object.<String, User[]>>}
* A promise which resolves with all user objects available to the
* current user, as a map of @link{User} arrays grouped by the
* identifier of their corresponding data source.
*/
service.getAllUsers = function getAllUsers(dataSources, permissionTypes) {
var deferred = $q.defer();
var userRequests = [];
var userArrays = {};
// Retrieve all users from all data sources
angular.forEach(dataSources, function retrieveUsers(dataSource) {
userRequests.push(
service.getUsers(dataSource, permissionTypes)
.success(function usersRetrieved(users) {
userArrays[dataSource] = users;
})
);
});
// Resolve when all requests are completed
$q.all(userRequests)
.then(
// All requests completed successfully
function allUsersRetrieved() {
deferred.resolve(userArrays);
},
// At least one request failed
function userRetrievalFailed(e) {
deferred.reject(e);
}
);
return deferred.promise;
};
/**
* Makes a request to the REST API to get the user having the given
* username, returning a promise that provides the corresponding

View File

@@ -43,6 +43,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
// Required services
var $location = $injector.get('$location');
var authenticationService = $injector.get('authenticationService');
var dataSourceService = $injector.get('dataSourceService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
var userService = $injector.get('userService');
@@ -193,7 +194,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
};
// Retrieve current permissions
permissionService.getAllPermissions(dataSources, currentUsername)
dataSourceService.apply(permissionService.getPermissions, dataSources, currentUsername)
.then(function permissionsRetrieved(permissions) {
// Store retrieved permissions
@@ -206,7 +207,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
});
// Retrieve all users for whom we have UPDATE or DELETE permission
userService.getAllUsers(dataSources, [
dataSourceService.apply(userService.getUsers, dataSources, [
PermissionSet.ObjectPermissionType.UPDATE,
PermissionSet.ObjectPermissionType.DELETE
])