Skip to content

Commit

Permalink
TP: adds tenant tree to replace flat, hard to visualize tenants table (
Browse files Browse the repository at this point in the history
…#5393)

* adds tenant tree instead of table

* adds the ability to export the tenant info as a CSV file

* adds tests for tenants view

* adds a changelog entry

* adds action buttons to each tenant item

* prepopulates the parent when creating a child tenant

* for a tenant, provides the ability to view ALL delivery services accessible to the tenant OR just the ones directly assigned to the tenant.

* fixes success messages when deleting a tenant

* only show all ds's if value is true

* removes drag cursor from tenant tree

* adds missing carriage return

* adds tenant tests to gha
  • Loading branch information
mitchell852 committed Dec 30, 2020
1 parent c2ad1ff commit 9a06d61
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 60 deletions.
3 changes: 2 additions & 1 deletion .github/actions/tp-e2e-tests/conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"servers/servers-spec.js",
"topologies/topologies-spec.js",
"deliveryServices/delivery-services-spec.js",
"jobs/jobs-spec.js"
"jobs/jobs-spec.js",
"tenants/tenants-spec.js"
]
}
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

## [unreleased]
### Added
- Traffic Portal: [#5394](https://github.com/apache/trafficcontrol/issues/5394) - Converts the tenant table to a tenant tree for usability
- Traffic Ops: added a feature so that the user can specify `maxRequestHeaderBytes` on a per delivery service basis
- Traffic Router: log warnings when requests to Traffic Monitor return a 503 status code
- [#5344](https://github.com/apache/trafficcontrol/issues/5344) - Add a page that addresses migrating from Traffic Ops API v1 for each endpoint
Expand Down
1 change: 0 additions & 1 deletion traffic_portal/app/src/common/api/TenantService.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ var TenantService = function($http, ENV, messageModel) {
this.deleteTenant = function(id) {
return $http.delete(ENV.api['root'] + "tenants/" + id).then(
function(result) {
messageModel.setMessages([ { level: 'success', text: 'Tenant deleted' } ], true);
return result;
},
function(err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ var FormTenantController = function(tenant, $scope, $location, formUtils, tenant
$location.path($location.path() + '/users');
};

$scope.viewDSs = function() {
$location.path($location.path() + '/delivery-services');
};

$scope.navigateToPath = locationUtils.navigateToPath;

$scope.hasError = formUtils.hasError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
* under the License.
*/

var FormEditTenantController = function(tenant, $scope, $controller, $uibModal, $anchorScroll, locationUtils, tenantService) {
var FormEditTenantController = function(tenant, $scope, $controller, $uibModal, $anchorScroll, locationUtils, tenantService, messageModel) {

// extends the FormTenantController to inherit common methods
angular.extend(this, $controller('FormTenantController', { tenant: tenant, $scope: $scope }));

var deleteTenant = function(tenant) {
tenantService.deleteTenant(tenant.id)
.then(function() {
.then(function(result) {
messageModel.setMessages(result.data.alerts, true);
locationUtils.navigateToPath('/tenants');
});
};
Expand Down Expand Up @@ -68,5 +69,5 @@ var FormEditTenantController = function(tenant, $scope, $controller, $uibModal,

};

FormEditTenantController.$inject = ['tenant', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'tenantService'];
FormEditTenantController.$inject = ['tenant', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'tenantService', 'messageModel'];
module.exports = FormEditTenantController;
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,17 @@
<li class="active">{{tenantName}}</li>
</ol>
<div class="pull-right" role="group" ng-show="!settings.isNew">
<button class="btn btn-primary" title="View Users" ng-click="viewUsers()">View Users</button>
<button class="btn btn-primary" title="View Delivery Services" ng-click="viewDSs()">View Delivery Services</button>
<button class="btn btn-default" title="View Users" ng-click="viewUsers()">View Users</button>
<div class="btn-group" role="group" uib-dropdown is-open="more.isopen">
<button name="moreBtn" type="button" class="btn btn-default dropdown-toggle" uib-dropdown-toggle aria-haspopup="true" aria-expanded="false">
View Delivery Services&nbsp;
<span class="caret"></span>
</button>
<ul class="dropdown-menu-right dropdown-menu" uib-dropdown-menu>
<li role="menuitem"><a href="/#!/tenants/{{tenant.id}}/delivery-services">Assigned To Tenant</a></li>
<li role="menuitem"><a href="/#!/tenants/{{tenant.id}}/delivery-services?all=true">Accessible To Tenant</a></li>
</ul>
</div>
</div>
<div class="clearfix"></div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ var FormUserController = function(user, $scope, $location, formUtils, stringUtil

$scope.labelize = stringUtils.labelize;

$scope.viewDeliveryServices = function() {
$location.path('/tenants/' + user.tenantId + '/delivery-services');
};

$scope.navigateToPath = locationUtils.navigateToPath;

$scope.hasError = formUtils.hasError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<li class="active">{{userName}}</li>
</ol>
<div class="pull-right" role="group" ng-show="!settings.isNew">
<button class="btn btn-primary" title="View Delivery Services" ng-click="viewDeliveryServices()">View Delivery Services</button>
<a class="btn btn-primary" ng-href="/#!/tenants/{{user.tenantId}}/delivery-services?all=true">View Delivery Services</a>
</div>
<div class="clearfix"></div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,63 @@
* under the License.
*/

var TableTenantsController = function(currentUserTenant, tenants, $scope, $state, $timeout, locationUtils, tenantUtils) {
var TableTenantsController = function(currentUserTenant, tenants, $scope, $state, $timeout, $uibModal, locationUtils, fileUtils, tenantUtils, tenantService, messageModel) {

$scope.isUserTenant = function(tenant) {
return tenant.id == currentUserTenant.id;
};
$scope.tenantTree = [];

$scope.editTenant = function(id) {
locationUtils.navigateToPath('/tenants/' + id);
$scope.hasChildren = function(node) {
return node.children.length > 0;
};

$scope.createTenant = function() {
locationUtils.navigateToPath('/tenants/new');
$scope.toggle = function(scope) {
scope.toggle();
};

var init = function() {
$scope.createTenant = function(parentId) {
if (parentId) {
locationUtils.navigateToPath('/tenants/new?parentId=' + parentId);
} else {
locationUtils.navigateToPath('/tenants/new');
}
};

$scope.tenants = tenantUtils.hierarchySort(tenantUtils.groupTenantsByParent(tenants), currentUserTenant.parentId, []);
tenantUtils.addLevels($scope.tenants);
$scope.exportCSV = function() {
fileUtils.convertToCSV(tenants, 'Tenants', ['id', 'lastUpdated', 'name', 'active', 'parentId', 'parentName']);
};

$timeout(function () {
$('#tenantsTable').dataTable({
"aLengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
"iDisplayLength": -1,
"bSort": false
});
}, 100);
$scope.confirmDelete = function(tenant) {
const params = {
title: 'Delete Tenant: ' + tenant.name,
key: tenant.name
};
const modalInstance = $uibModal.open({
templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
controller: 'DialogDeleteController',
size: 'md',
resolve: {
params: function () {
return params;
}
}
});
modalInstance.result.then(function() {
tenantService.deleteTenant(tenant.id)
.then(function(result) {
messageModel.setMessages(result.data.alerts, false);
$state.reload();
});
}, function () {
// do nothing
});
};

let init = function() {
$scope.tenants = tenantUtils.hierarchySort(tenantUtils.groupTenantsByParent(tenants), currentUserTenant.parentId, []);
$scope.tenantTree = tenantUtils.convertToHierarchy($scope.tenants);
};
init();

};

TableTenantsController.$inject = ['currentUserTenant', 'tenants', '$scope', '$state', '$timeout', 'locationUtils', 'tenantUtils'];
TableTenantsController.$inject = ['currentUserTenant', 'tenants', '$scope', '$state', '$timeout', '$uibModal', 'locationUtils', 'fileUtils', 'tenantUtils', 'tenantService', 'messageModel'];
module.exports = TableTenantsController;
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,58 @@
<div class="x_panel">
<div class="x_title">
<ol class="breadcrumb pull-left">
<li class="active">Tenants</li>
<li class="active">Tenants <small>[{{tenants.length}}]</small></li>
</ol>
<div class="pull-right">
<button class="btn btn-primary" title="Create New Tenant" ng-click="createTenant()"><i class="fa fa-plus"></i></button>
<button class="btn btn-default" title="Refresh" ng-click="refresh()"><i class="fa fa-refresh"></i></button>
<button name="createTenantButton" class="btn btn-primary" title="Create New Tenant" ng-click="createTenant()"><i class="fa fa-plus"></i></button>
<div class="btn-group" role="group" uib-dropdown is-open="more.isopen">
<button name="moreBtn" type="button" class="btn btn-default dropdown-toggle" uib-dropdown-toggle aria-haspopup="true" aria-expanded="false">
More&nbsp;
<span class="caret"></span>
</button>
<ul class="dropdown-menu-right dropdown-menu" uib-dropdown-menu>
<li role="menuitem"><a name="createServerMenuItem" ng-click="exportCSV()">Export Tenant CSV</a></li>
</ul>
</div>
</div>
<div class="clearfix"></div>
</div>
<div class="x_content">
<br>
<table id="tenantsTable" class="table responsive-utilities jambo_table">
<thead>
<tr class="headings">
<th>Name</th>
<th>Active</th>
<th>Parent</th>
</tr>
</thead>
<tbody>
<tr ng-click="editTenant(t.id)" ng-repeat="t in ::tenants" ng-class="::{'active': isUserTenant(t)}">
<td name="name" data-search="^{{::t.name}}$">{{::t.name}}</td>
<td data-search="^{{::t.active}}$">{{::t.active}}</td>
<td data-search="^{{::t.parentName}}$">{{::t.parentName}}</td>
</tr>
</tbody>
</table>
<div id="tenant-tree-root" ui-tree data-drag-enabled="false">
<ol ui-tree-nodes ng-model="tenantTree">
<li ng-repeat="node in tenantTree" ui-tree-node ng-include="'nodes_renderer.html'"></li>
</ol>
</div>
</div>
</div>

<script type="text/ng-template" id="nodes_renderer.html">
<div ui-tree-handle class="tree-node tree-node-content">
<div class="tree-node-label pull-left">
<a class="tree-toggle btn btn-primary btn-xs" ng-if="hasChildren(node)" ng-click="toggle(this)">
<i class="fa" ng-class="collapsed ? 'fa-caret-right' : 'fa-caret-down'"></i>
</a> <a ng-href="#!/tenants/{{node.id}}">{{::node.name}}</a>
</div>
<div class="pull-right">
<a title="Add child tenant" class="btn btn-primary btn-xs" data-nodrag ng-click="createTenant(node.id)" style="margin-right: 8px;">
<i class="fa fa-plus"></i>
</a>
<a title="Delete tenant" class="btn btn-danger btn-xs" data-nodrag ng-click="confirmDelete(node)" style="margin-right: 8px;">
<i class="fa fa-times"></i>
</a>
<a title="Edit tenant" class="btn btn-default btn-xs" data-nodrag ng-href="#!/tenants/{{node.id}}">
<i class="fa fa-edit"></i>
</a>
</div>
</div>
<ol ui-tree-nodes="" ng-model="node.children" ng-class="{ hidden: collapsed }">
<li ng-repeat="node in node.children" ui-tree-node ng-include="'nodes_renderer.html'"></li>
</ol>
</script>






Expand Down
50 changes: 50 additions & 0 deletions traffic_portal/app/src/common/service/utils/FileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,56 @@ var FileUtils = function() {
a.remove();
};

this.convertToCSV = function(JSONData, reportTitle, includedKeys) {
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
var CSV = '';
CSV += reportTitle + '\r\n\r\n';

var keys = [];
for (var key in arrData[0]) {
if (!includedKeys || _.contains(includedKeys, key)) {
keys.push(key);
}
}
keys.sort(); // alphabetically

var row = "";
for (var i = 0; i < keys.length; i++) {
row += keys[i] + ',';
}
row = row.slice(0, -1);

CSV += row + '\r\n';

for (var j = 0; j < arrData.length; j++) {
var row = "";
for (var k = 0; k < keys.length; k++) {
row += '"' + arrData[j][keys[k]] + '",';
}
row.slice(0, row.length - 1);
CSV += row + '\r\n';
}

if (CSV == '') {
alert("Invalid data");
return;
}

var fileName = "";
fileName += reportTitle.replace(/ /g,"_");

var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
var link = document.createElement("a");
link.href = uri;

link.style = "visibility:hidden";
link.download = fileName + ".csv";

document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};

};

FileUtils.$inject = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = angular.module('trafficPortal.private.tenants.deliveryServices'
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('trafficPortal.private.tenants.deliveryServices', {
url: '/{tenantId}/delivery-services',
url: '/{tenantId}/delivery-services?all',
views: {
tenantsContent: {
templateUrl: 'common/modules/table/tenantDeliveryServices/table.tenantDeliveryServices.tpl.html',
Expand All @@ -30,8 +30,11 @@ module.exports = angular.module('trafficPortal.private.tenants.deliveryServices'
tenant: function($stateParams, tenantService) {
return tenantService.getTenant($stateParams.tenantId);
},
deliveryServices: function(tenant, deliveryServiceService) {
return deliveryServiceService.getDeliveryServices({ accessibleTo: tenant.id });
deliveryServices: function($stateParams, tenant, deliveryServiceService) {
if ($stateParams.all && $stateParams.all === 'true') {
return deliveryServiceService.getDeliveryServices({ accessibleTo: tenant.id });
}
return deliveryServiceService.getDeliveryServices({ tenant: tenant.id });
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions traffic_portal/app/src/modules/private/tenants/new/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ module.exports = angular.module('trafficPortal.private.tenants.new', [])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('trafficPortal.private.tenants.new', {
url: '/new',
url: '/new?parentId',
views: {
tenantsContent: {
templateUrl: 'common/modules/form/tenant/form.tenant.tpl.html',
controller: 'FormNewTenantController',
resolve: {
tenant: function() {
tenant: function($stateParams) {
if ($stateParams.parentId) {
return { parentId: parseInt($stateParams.parentId, 10) };
}
return {};
}
}
Expand Down
6 changes: 6 additions & 0 deletions traffic_portal/app/src/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,9 @@ input[type="checkbox"].dirty {
}

}

#tenant-tree-root {
.angular-ui-tree-handle {
cursor: default !important;
}
}
3 changes: 2 additions & 1 deletion traffic_portal/test/end_to_end/conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"servers/servers-spec.js",
"topologies/topologies-spec.js",
"deliveryServices/delivery-services-spec.js",
"jobs/jobs-spec.js"
"jobs/jobs-spec.js",
"tenants/tenants-spec.js"
]
}
}
Loading

0 comments on commit 9a06d61

Please sign in to comment.