mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-22 02:02:14 +00:00
Performance tuning
- Add `getGroupsAbridged` which returns a subset of group data for dropdowns and other places where all groups are listed - Remove unnecessary checks for `canSeeGroup` in `groups.scala.html` since all users can see all groups - Move `ZoneController` initialization in `manageZone.scala.html` to higher level to avoid waiting for groups to load when expanding the select box - Add `PreparePortalHook` to automatically run `prepare-portal.sh` when `project porta; run` is executed
This commit is contained in:
parent
d9f986997d
commit
02d702f461
13
build.sbt
13
build.sbt
@ -2,10 +2,9 @@ import CompilerOptions._
|
||||
import Dependencies._
|
||||
import microsites._
|
||||
import org.scalafmt.sbt.ScalafmtPlugin._
|
||||
import scoverage.ScoverageKeys.{coverageFailOnMinimum, coverageMinimum}
|
||||
import scoverage.ScoverageKeys.{coverageMinimum, coverageFailOnMinimum}
|
||||
|
||||
import scala.language.postfixOps
|
||||
import scala.sys.env
|
||||
import scala.util.Try
|
||||
|
||||
lazy val IntegrationTest = config("it").extend(Test)
|
||||
@ -38,7 +37,7 @@ lazy val sharedSettings = Seq(
|
||||
// coverage options
|
||||
coverageMinimum := 85,
|
||||
coverageFailOnMinimum := true,
|
||||
coverageHighlighting := true,
|
||||
coverageHighlighting := true
|
||||
)
|
||||
|
||||
lazy val testSettings = Seq(
|
||||
@ -206,9 +205,11 @@ lazy val portalSettings = Seq(
|
||||
routesGenerator := InjectedRoutesGenerator,
|
||||
coverageExcludedPackages := "<empty>;views.html.*;router.*;controllers\\.javascript.*;.*Reverse.*",
|
||||
javaOptions in Test += "-Dconfig.file=conf/application-test.conf",
|
||||
// ads the version when working locally with sbt run
|
||||
// Adds the version when working locally with sbt run
|
||||
PlayKeys.devSettings += "vinyldns.base-version" -> (version in ThisBuild).value,
|
||||
// adds an extra classpath to the portal loading so we can externalize jars, make sure to create the lib_extra
|
||||
// Automatically run the prepare portal script before `run`
|
||||
PlayKeys.playRunHooks += PreparePortalHook(baseDirectory.value),
|
||||
// Adds an extra classpath to the portal loading so we can externalize jars, make sure to create the lib_extra
|
||||
// directory and lay down any dependencies that are required when deploying
|
||||
scriptClasspath in bashScriptDefines ~= (cp => cp :+ "lib_extra/*"),
|
||||
mainClass in reStart := None,
|
||||
@ -241,7 +242,7 @@ lazy val portal = (project in file("modules/portal"))
|
||||
.settings(testSettings)
|
||||
.settings(portalSettings)
|
||||
.settings(
|
||||
name := "portal",
|
||||
name := "portal"
|
||||
)
|
||||
.dependsOn(mysql)
|
||||
|
||||
|
@ -17,8 +17,8 @@
|
||||
package vinyldns.api.domain.membership
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import vinyldns.core.domain.auth.AuthPrincipal
|
||||
import vinyldns.core.domain.membership.GroupChangeType.GroupChangeType
|
||||
import vinyldns.core.domain.membership.GroupStatus.GroupStatus
|
||||
import vinyldns.core.domain.membership.LockStatus.LockStatus
|
||||
@ -36,15 +36,20 @@ final case class GroupInfo(
|
||||
admins: Set[UserId] = Set.empty
|
||||
)
|
||||
object GroupInfo {
|
||||
def apply(group: Group): GroupInfo = GroupInfo(
|
||||
def apply(group: Group): GroupInfo = fromGroup(group, abridged = false, None)
|
||||
|
||||
def fromGroup(group: Group, abridged: Boolean = false,
|
||||
authPrincipal: Option[AuthPrincipal]): GroupInfo = GroupInfo(
|
||||
id = group.id,
|
||||
name = group.name,
|
||||
email = group.email,
|
||||
description = group.description,
|
||||
created = group.created,
|
||||
status = group.status,
|
||||
members = group.memberIds.map(UserId),
|
||||
admins = group.adminUserIds.map(UserId)
|
||||
created = if (abridged) null else group.created,
|
||||
status = if (abridged) null else group.status,
|
||||
members = (if (abridged && authPrincipal.isDefined) group.memberIds.filter(x => authPrincipal.get.userId == x && authPrincipal.get.isGroupMember(group.id))
|
||||
else group.memberIds).map(UserId),
|
||||
admins = (if (abridged && authPrincipal.isDefined) group.adminUserIds.filter(x => authPrincipal.get.userId == x && authPrincipal.get.isGroupAdmin(group))
|
||||
else group.memberIds).map(UserId)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,8 @@ class MembershipService(
|
||||
startFrom: Option[String],
|
||||
maxItems: Int,
|
||||
authPrincipal: AuthPrincipal,
|
||||
ignoreAccess: Boolean
|
||||
ignoreAccess: Boolean,
|
||||
abridged: Boolean = false
|
||||
): Result[ListMyGroupsResponse] = {
|
||||
val groupsCall =
|
||||
if (authPrincipal.isSystemAdmin || ignoreAccess) {
|
||||
@ -198,7 +199,7 @@ class MembershipService(
|
||||
}
|
||||
|
||||
groupsCall.map { grp =>
|
||||
pageListGroupsResponse(grp.toList, groupNameFilter, startFrom, maxItems, ignoreAccess)
|
||||
pageListGroupsResponse(grp.toList, groupNameFilter, startFrom, maxItems, ignoreAccess, abridged, authPrincipal)
|
||||
}
|
||||
}.toResult
|
||||
|
||||
@ -207,12 +208,14 @@ class MembershipService(
|
||||
groupNameFilter: Option[String],
|
||||
startFrom: Option[String],
|
||||
maxItems: Int,
|
||||
ignoreAccess: Boolean
|
||||
): ListMyGroupsResponse = {
|
||||
ignoreAccess: Boolean,
|
||||
abridged: Boolean = false,
|
||||
authPrincipal: AuthPrincipal
|
||||
): ListMyGroupsResponse = {
|
||||
val allMyGroups = allGroups
|
||||
.filter(_.status == GroupStatus.Active)
|
||||
.sortBy(_.id)
|
||||
.map(GroupInfo.apply)
|
||||
.map(x => GroupInfo.fromGroup(x, abridged, Some(authPrincipal)))
|
||||
|
||||
val filtered = allMyGroups
|
||||
.filter(grp => groupNameFilter.forall(grp.name.contains(_)))
|
||||
|
@ -44,7 +44,8 @@ trait MembershipServiceAlgebra {
|
||||
startFrom: Option[String],
|
||||
maxItems: Int,
|
||||
authPrincipal: AuthPrincipal,
|
||||
ignoreAccess: Boolean
|
||||
ignoreAccess: Boolean,
|
||||
abridged: Boolean = false
|
||||
): Result[ListMyGroupsResponse]
|
||||
|
||||
def listMembers(
|
||||
|
@ -82,13 +82,15 @@ class MembershipRoute(
|
||||
"startFrom".?,
|
||||
"maxItems".as[Int].?(DEFAULT_MAX_ITEMS),
|
||||
"groupNameFilter".?,
|
||||
"ignoreAccess".as[Boolean].?(false)
|
||||
"ignoreAccess".as[Boolean].?(false),
|
||||
"abridged".as[Boolean].?(false),
|
||||
) {
|
||||
(
|
||||
startFrom: Option[String],
|
||||
maxItems: Int,
|
||||
groupNameFilter: Option[String],
|
||||
ignoreAccess: Boolean
|
||||
ignoreAccess: Boolean,
|
||||
abridged: Boolean
|
||||
) =>
|
||||
{
|
||||
handleRejections(invalidQueryHandler) {
|
||||
@ -101,7 +103,7 @@ class MembershipRoute(
|
||||
) {
|
||||
authenticateAndExecute(
|
||||
membershipService
|
||||
.listMyGroups(groupNameFilter, startFrom, maxItems, _, ignoreAccess)
|
||||
.listMyGroups(groupNameFilter, startFrom, maxItems, _, ignoreAccess, abridged)
|
||||
) { groups =>
|
||||
complete(StatusCodes.OK, groups)
|
||||
}
|
||||
|
@ -79,8 +79,7 @@
|
||||
<tbody>
|
||||
<tr ng-repeat="group in groups.items | orderBy:'+name'">
|
||||
<td class="wrap-long-text">
|
||||
<a ng-if="canSeeGroup(group)" ng-href="/groups/{{group.id}}">{{group.name}}</a>
|
||||
<span ng-if="!canSeeGroup(group)">{{group.name}}</span>
|
||||
<a ng-href="/groups/{{group.id}}">{{group.name}}</a>
|
||||
</td>
|
||||
<td class="wrap-long-text">{{group.email}}</td>
|
||||
<td class="wrap-long-text">{{group.description}}</td>
|
||||
|
@ -352,7 +352,7 @@
|
||||
<!-- START ACL MODAL -->
|
||||
<form name="addAclRuleForm" role="form" class="form-horizontal" novalidate>
|
||||
<modal modal-id="acl_modal" modal-title="{{ aclModal.title }}">
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" ng-controller="ZonesController">
|
||||
<modal-element label="Apply Rule to" invalid-when="addAclRuleForm.$submitted && addAclRuleForm.priority.$invalid">
|
||||
<select name="priority"
|
||||
id="acl-rule-priority"
|
||||
@ -373,10 +373,9 @@
|
||||
<select name="groupId"
|
||||
class="form-control"
|
||||
ng-model="currentAclRule.groupId"
|
||||
ng-controller="ZonesController"
|
||||
ng-disabled="aclModal.details.readOnly"
|
||||
required>
|
||||
<option ng-repeat="group in allGroups" value="{{ group.id }}">
|
||||
<option ng-repeat="group in allGroups | orderBy:'+name'" value="{{ group.id }}">
|
||||
{{group.name}} ({{group.description}})</option>
|
||||
<option value="">--- Please select a group ---</option>
|
||||
</select>
|
||||
|
@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
angular.module('controller.groups', []).controller('GroupsController', function ($scope, $log, $location, groupsService, profileService, utilityService) {
|
||||
//registering bootstrap modal close event to refresh data after create group action
|
||||
//registering bootstrap modal close event to refresh data after create group action
|
||||
angular.element('#modal_new_group').one('hide.bs.modal', function () {
|
||||
$scope.closeModal();
|
||||
});
|
||||
|
||||
$scope.groups = { items: [] };
|
||||
$scope.groups = {items: []};
|
||||
$scope.groupsLoaded = false;
|
||||
$scope.alerts = [];
|
||||
$scope.ignoreAccess = false;
|
||||
@ -37,26 +37,26 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
//shared modal
|
||||
var modalDialog;
|
||||
|
||||
$scope.openModal = function(evt){
|
||||
$scope.openModal = function (evt) {
|
||||
$scope.currentGroup = {};
|
||||
void(evt && evt.preventDefault());
|
||||
if(!modalDialog){
|
||||
void (evt && evt.preventDefault());
|
||||
if (!modalDialog) {
|
||||
modalDialog = angular.element('#modal_new_group').modal();
|
||||
}
|
||||
modalDialog.modal('show');
|
||||
};
|
||||
|
||||
$scope.closeModal = function(evt){
|
||||
void(evt && evt.preventDefault());
|
||||
if(!modalDialog){
|
||||
$scope.closeModal = function (evt) {
|
||||
void (evt && evt.preventDefault());
|
||||
if (!modalDialog) {
|
||||
modalDialog = angular.element('#modal_new_group').modal();
|
||||
}
|
||||
modalDialog.modal('hide');
|
||||
return true;
|
||||
};
|
||||
|
||||
$scope.closeEditModal = function(evt){
|
||||
void(evt && evt.preventDefault());
|
||||
$scope.closeEditModal = function (evt) {
|
||||
void (evt && evt.preventDefault());
|
||||
editModalDialog = angular.element('#modal_edit_group').modal();
|
||||
editModalDialog.modal('hide');
|
||||
$scope.reset();
|
||||
@ -68,7 +68,7 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
//prevent user executing service call multiple times
|
||||
//if true prevent, if false allow for execution of rest of code
|
||||
//ng-href='/groups'
|
||||
$log.log('createGroup::called', $scope.data);
|
||||
$log.log('createGroup::called', $scope.data);
|
||||
|
||||
if ($scope.processing) {
|
||||
$log.log('createGroup::processing is true; exiting');
|
||||
@ -83,14 +83,14 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
'name': name,
|
||||
'email': email,
|
||||
'description': description,
|
||||
'members': [{ id: $scope.profile.id }],
|
||||
'admins': [{ id: $scope.profile.id }]
|
||||
'members': [{id: $scope.profile.id}],
|
||||
'admins': [{id: $scope.profile.id}]
|
||||
};
|
||||
|
||||
//create group success callback
|
||||
function success(response) {
|
||||
var alert = utilityService.success('Successfully Created Group: ' + name, response, 'createGroup::createGroup successful');
|
||||
$scope.alerts.push(alert);
|
||||
var alert = utilityService.success('Successfully Created Group: ' + name, response, 'createGroup::createGroup successful');
|
||||
$scope.alerts.push(alert);
|
||||
$scope.closeModal();
|
||||
$scope.reset();
|
||||
$scope.refresh();
|
||||
@ -99,17 +99,17 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
|
||||
return groupsService.createGroup(payload)
|
||||
.then(success)
|
||||
.catch(function (error){
|
||||
.catch(function (error) {
|
||||
handleError(error, 'groupsService::createGroup-failure');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.allGroups = function() {
|
||||
$scope.allGroups = function () {
|
||||
$scope.ignoreAccess = true;
|
||||
$scope.refresh();
|
||||
}
|
||||
|
||||
$scope.myGroups = function() {
|
||||
$scope.myGroups = function () {
|
||||
$scope.ignoreAccess = false;
|
||||
$scope.refresh();
|
||||
}
|
||||
@ -121,11 +121,12 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
$scope.groups.items = result.groups;
|
||||
$scope.groupsLoaded = true;
|
||||
if (!$scope.query.length) {
|
||||
$scope.hasGroups = response.data.groups.length > 0;
|
||||
$scope.hasGroups = $scope.groups.items.length > 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getGroups($scope.ignoreAccess)
|
||||
|
||||
getGroupsAbridged($scope.ignoreAccess)
|
||||
.then(success)
|
||||
.catch(function (error) {
|
||||
handleError(error, 'getGroups::refresh-failure');
|
||||
@ -151,30 +152,43 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
$log.log('groupsService::getGroups-success');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
return groupsService
|
||||
.getGroups($scope.ignoreAccess, $scope.query)
|
||||
.then(success)
|
||||
.catch(function (error){
|
||||
.catch(function (error) {
|
||||
handleError(error, 'groupsService::getGroups-failure');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getGroupsAbridged() {
|
||||
function success(response) {
|
||||
$log.log('groupsService::getGroups-success');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
return groupsService
|
||||
.getGroupsAbridged($scope.ignoreAccess, $scope.query)
|
||||
.then(success)
|
||||
.catch(function (error) {
|
||||
handleError(error, 'groupsService::getGroups-failure');
|
||||
});
|
||||
}
|
||||
|
||||
// Return true if there are no groups created by the user
|
||||
$scope.haveNoGroups = function(groupLength){
|
||||
if(!$scope.hasGroups && !groupLength && $scope.groupsLoaded && $scope.query.length == ""){
|
||||
$scope.haveNoGroups = function (groupLength) {
|
||||
if (!$scope.hasGroups && !groupLength && $scope.groupsLoaded && $scope.query.length == "") {
|
||||
return true
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if no groups are found related to the search query
|
||||
$scope.searchCriteria = function(groupLength){
|
||||
if($scope.groupsLoaded && !groupLength && $scope.query.length != ""){
|
||||
$scope.searchCriteria = function (groupLength) {
|
||||
if ($scope.groupsLoaded && !groupLength && $scope.query.length != "") {
|
||||
return true
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -188,7 +202,7 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
//prevent user executing service call multiple times
|
||||
//if true prevent, if false allow for execution of rest of code
|
||||
//ng-href='/groups'
|
||||
$log.log('updateGroup::called', $scope.data);
|
||||
$log.log('updateGroup::called', $scope.data);
|
||||
|
||||
if ($scope.processing) {
|
||||
$log.log('updateGroup::processing is true; exiting');
|
||||
@ -213,17 +227,17 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
|
||||
//update group success callback
|
||||
function success(response) {
|
||||
var alert = utilityService.success('Successfully Updated Group: ' + name, response, 'updateGroup::updateGroup successful');
|
||||
$scope.alerts.push(alert);
|
||||
$scope.closeEditModal();
|
||||
$scope.reset();
|
||||
$scope.refresh();
|
||||
return response.data;
|
||||
var alert = utilityService.success('Successfully Updated Group: ' + name, response, 'updateGroup::updateGroup successful');
|
||||
$scope.alerts.push(alert);
|
||||
$scope.closeEditModal();
|
||||
$scope.reset();
|
||||
$scope.refresh();
|
||||
return response.data;
|
||||
}
|
||||
|
||||
return groupsService.updateGroup($scope.currentGroup.id, payload)
|
||||
.then(success)
|
||||
.catch(function (error){
|
||||
.catch(function (error) {
|
||||
handleError(error, 'groupsService::updateGroup-failure');
|
||||
});
|
||||
};
|
||||
@ -234,17 +248,18 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
};
|
||||
|
||||
$scope.submitDeleteGroup = function () {
|
||||
function success (response){
|
||||
function success(response) {
|
||||
$("#delete_group_modal").modal("hide");
|
||||
$scope.refresh();
|
||||
var alert = utilityService.success('Removed Group: ' + $scope.currentGroup.name, response, 'groupsService::deleteGroup successful');
|
||||
$scope.alerts.push(alert);
|
||||
}
|
||||
|
||||
groupsService.deleteGroups($scope.currentGroup.id)
|
||||
.then(success)
|
||||
.catch(function (error){
|
||||
handleError(error, 'groupsService::deleteGroup-failure');
|
||||
});
|
||||
.then(success)
|
||||
.catch(function (error) {
|
||||
handleError(error, 'groupsService::deleteGroup-failure');
|
||||
});
|
||||
};
|
||||
|
||||
function profileSuccess(results) {
|
||||
@ -263,15 +278,15 @@ angular.module('controller.groups', []).controller('GroupsController', function
|
||||
$scope.profile = $scope.profile || {};
|
||||
}
|
||||
|
||||
$scope.groupAdmin = function(group) {
|
||||
$scope.groupAdmin = function (group) {
|
||||
var isAdmin = group.admins.find(function (x) {
|
||||
return x.id === $scope.profile.id;
|
||||
return x.id === $scope.profile.id;
|
||||
});
|
||||
var isSuper = $scope.profile.isSuper;
|
||||
return isAdmin || isSuper;
|
||||
}
|
||||
|
||||
$scope.canSeeGroup = function(group) {
|
||||
$scope.canSeeGroup = function (group) {
|
||||
var isMember = group.members.some(x => x.id === $scope.profile.id);
|
||||
var isSupport = $scope.profile.isSupport;
|
||||
var isSuper = $scope.profile.isSuper;
|
||||
|
@ -53,15 +53,7 @@ angular.module('controller.zones', [])
|
||||
$scope.currentZone.transferConnection = {};
|
||||
};
|
||||
|
||||
groupsService.getGroups().then(function (results) {
|
||||
if (results.data) {
|
||||
$scope.myGroups = results.data.groups;
|
||||
$scope.myGroupIds = results.data.groups.map(function(grp) {return grp['id']});
|
||||
}
|
||||
$scope.resetCurrentZone();
|
||||
});
|
||||
|
||||
groupsService.getGroups(true, "").then(function (results) {
|
||||
groupsService.getGroupsAbridged(true, "").then(function (results) {
|
||||
if (results.data) {
|
||||
$scope.allGroups = results.data.groups;
|
||||
}
|
||||
|
@ -84,6 +84,21 @@ angular.module('service.groups', [])
|
||||
return $http.get(url);
|
||||
};
|
||||
|
||||
this.getGroupsAbridged = function (ignoreAccess, query) {
|
||||
if (query == "") {
|
||||
query = null;
|
||||
}
|
||||
var params = {
|
||||
"maxItems": 1500,
|
||||
"groupNameFilter": query,
|
||||
"ignoreAccess": ignoreAccess,
|
||||
"abridged": true
|
||||
};
|
||||
var url = '/api/groups';
|
||||
url = this.urlBuilder(url, params);
|
||||
return $http.get(url);
|
||||
};
|
||||
|
||||
this.getGroupListChanges = function (id, count, groupId) {
|
||||
var url = '/api/groups/' + groupId + '/changes';
|
||||
url = this.urlBuilder(url, { 'startFrom': id, 'maxItems': count });
|
||||
|
14
project/PreparePortalHook.scala
Normal file
14
project/PreparePortalHook.scala
Normal file
@ -0,0 +1,14 @@
|
||||
import scala.sys.process.Process
|
||||
import play.sbt.PlayRunHook
|
||||
import sbt.File
|
||||
|
||||
object PreparePortalHook {
|
||||
def apply(base: File): PlayRunHook = {
|
||||
object GruntProcess extends PlayRunHook {
|
||||
override def beforeStarted(): Unit = {
|
||||
Process("./prepare-portal.sh", base).!
|
||||
}
|
||||
}
|
||||
GruntProcess
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user