From fde96666474146e6e137e080073d8dce0d8ff8a8 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 1 Apr 2015 13:56:30 -0400 Subject: [PATCH] Add the team membership to the robots view --- data/model/legacy.py | 39 +++++++++++++-- endpoints/api/robot.py | 53 ++++++++++++++++----- static/css/directives/ui/avatar.css | 4 ++ static/css/directives/ui/robots-manager.css | 4 ++ static/directives/robots-manager.html | 37 ++++++++++---- static/js/directives/ui/robots-manager.js | 3 +- 6 files changed, 112 insertions(+), 28 deletions(-) diff --git a/data/model/legacy.py b/data/model/legacy.py index e140a1fbc..c8c97569b 100644 --- a/data/model/legacy.py +++ b/data/model/legacy.py @@ -310,15 +310,44 @@ def _list_entity_robots(entity_name): .where(User.robot == True, User.username ** (entity_name + '+%'))) -def list_entity_robot_tuples(entity_name): - return (_list_entity_robots(entity_name) +class _TupleWrapper(object): + def __init__(self, data, fields): + self._data = data + self._fields = fields + + def get(self, field): + return self._data[self._fields.index(field.name + ':' + field.model_class.__name__)] + + +class TupleSelector(object): + """ Helper class for selecting tuples from a peewee query and easily accessing + them as if they were objects. + """ + def __init__(self, query, fields): + self._query = query.select(*fields).tuples() + self._fields = [field.name + ':' + field.model_class.__name__ for field in fields] + + def __iter__(self): + return self._build_iterator() + + def _build_iterator(self): + for tuple_data in self._query: + yield _TupleWrapper(tuple_data, self._fields) + + + +def list_entity_robot_permission_teams(entity_name): + query = (_list_entity_robots(entity_name) .join(RepositoryPermission, JOIN_LEFT_OUTER, on=(RepositoryPermission.user == FederatedLogin.user)) .join(Repository, JOIN_LEFT_OUTER) .switch(User) - .group_by(User, FederatedLogin) - .select(User.username, FederatedLogin.service_ident, fn.Count(Repository.id)) - .tuples()) + .join(TeamMember, JOIN_LEFT_OUTER) + .join(Team, JOIN_LEFT_OUTER)) + + fields = [User.username, FederatedLogin.service_ident, Repository.name, Team.name] + return TupleSelector(query, fields) + def list_robot_permissions(robot_name): return (RepositoryPermission.select(RepositoryPermission, User, Repository) diff --git a/endpoints/api/robot.py b/endpoints/api/robot.py index 7d5136951..ecb26e573 100644 --- a/endpoints/api/robot.py +++ b/endpoints/api/robot.py @@ -5,15 +5,15 @@ from auth.permissions import AdministerOrganizationPermission, OrganizationMembe from auth.auth_context import get_authenticated_user from auth import scopes from data import model +from data.database import User, Team, Repository, FederatedLogin from util.names import format_robot_username from flask import abort +from app import avatar - -def robot_view(name, token, count=None): +def robot_view(name, token): return { 'name': name, - 'token': token, - 'permission_count': count + 'token': token } @@ -27,6 +27,41 @@ def permission_view(permission): } +def robots_list(prefix): + tuples = model.list_entity_robot_permission_teams(prefix) + + robots = {} + robot_teams = set() + + for robot_tuple in tuples: + robot_name = robot_tuple.get(User.username) + if not robot_name in robots: + robots[robot_name] = { + 'name': robot_name, + 'token': robot_tuple.get(FederatedLogin.service_ident), + 'teams': [], + 'repositories': [] + } + + team_name = robot_tuple.get(Team.name) + repository_name = robot_tuple.get(Repository.name) + + if team_name is not None: + check_key = robot_name + ':' + team_name + if not check_key in robot_teams: + robot_teams.add(check_key) + + robots[robot_name]['teams'].append({ + 'name': team_name, + 'avatar': avatar.get_data(team_name, team_name, 'team') + }) + + if repository_name is not None: + if not repository_name in robots[robot_name]['repositories']: + robots[robot_name]['repositories'].append(repository_name) + + return {'robots': robots.values()} + @resource('/v1/user/robots') @internal_only class UserRobotList(ApiResource): @@ -36,10 +71,7 @@ class UserRobotList(ApiResource): def get(self): """ List the available robots for the user. """ user = get_authenticated_user() - robots = model.list_entity_robot_tuples(user.username) - return { - 'robots': [robot_view(name, password, count) for name, password, count in robots] - } + return robots_list(user.username) @resource('/v1/user/robots/') @@ -85,10 +117,7 @@ class OrgRobotList(ApiResource): """ List the organization's robots. """ permission = OrganizationMemberPermission(orgname) if permission.can(): - robots = model.list_entity_robot_tuples(orgname) - return { - 'robots': [robot_view(name, password, count) for name, password, count in robots] - } + return robots_list(orgname) raise Unauthorized() diff --git a/static/css/directives/ui/avatar.css b/static/css/directives/ui/avatar.css index b0adf7748..0ba53cad5 100644 --- a/static/css/directives/ui/avatar.css +++ b/static/css/directives/ui/avatar.css @@ -23,4 +23,8 @@ font-style: normal !important; font-weight: normal !important; font-variant: normal !important; +} + +a .avatar-element .letter { + cursor: pointer !important; } \ No newline at end of file diff --git a/static/css/directives/ui/robots-manager.css b/static/css/directives/ui/robots-manager.css index 419246bff..3be428238 100644 --- a/static/css/directives/ui/robots-manager.css +++ b/static/css/directives/ui/robots-manager.css @@ -83,4 +83,8 @@ .robots-manager-element .repo-circle.no-background .fa-lock { bottom: 5px; right: 2px; +} + +.robots-manager-element .member-perm-summary { + margin-right: 14px; } \ No newline at end of file diff --git a/static/directives/robots-manager.html b/static/directives/robots-manager.html index 70dae29f7..c93ca24dd 100644 --- a/static/directives/robots-manager.html +++ b/static/directives/robots-manager.html @@ -28,16 +28,17 @@ - + - + + - - + diff --git a/static/js/directives/ui/robots-manager.js b/static/js/directives/ui/robots-manager.js index 9eb3dcf54..ff3463eaa 100644 --- a/static/js/directives/ui/robots-manager.js +++ b/static/js/directives/ui/robots-manager.js @@ -12,7 +12,7 @@ angular.module('quay').directive('robotsManager', function () { 'organization': '=organization', 'user': '=user' }, - controller: function($scope, $element, ApiService, $routeParams, CreateService) { + controller: function($scope, $element, ApiService, $routeParams, CreateService, Config) { $scope.ROBOT_PATTERN = ROBOT_PATTERN; // TODO(jschorr): move this to a service. @@ -26,6 +26,7 @@ angular.module('quay').directive('robotsManager', function () { $scope.loading = false; $scope.shownRobot = null; $scope.showRobotCounter = 0; + $scope.Config = Config; var loadRobotPermissions = function(info) { var shortName = $scope.getShortenedName(info.name);
Robot Account NameRepository PermissionsTeamsRepository Permissions
- + + @@ -49,13 +50,29 @@ {{ getPrefix(robotInfo.name) }}+{{ getShortenedName(robotInfo.name) }} - (No permissions on any repositories) - + + + (Not a member of any team) + + + + + + + + + + + (No permissions on any repositories) + + + Permissions on - {{ robotInfo.permission_count }} - repository - repositories + {{ robotInfo.repositories.length }} + repository + repositories