Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Commit

Permalink
TEAMFOUR-711: create space (#494)
Browse files Browse the repository at this point in the history
* WIP

* Refactored cluster actions as a directive

* Made input form names unique, started duplicate space name detection

* Create space implemented

* Removed debuggiong code

* Batch create spaces to optimize refresh of cache

* Readability

* Removed debuggiong code

* Readability

* lint

* Also make the creator an org user, also make the creator a space user. Things go funny otherwise

* Allow browsing Orgs and Spaces that the user has no roles in

* removed inline styles

* lint

* Fixed double class declaration!

* Fix duplication validator. Added margin bottom back to the org drop down
  • Loading branch information
julbra authored and richard-cox committed Jul 28, 2016
1 parent cc3623f commit 41c5f9b
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 95 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
*.iml
npm-debug.log
phantomjsdriver.log
node_modules
Expand Down
10 changes: 0 additions & 10 deletions src/app/view/endpoints/clusters/cluster/cluster.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
<div class="cluster">
<span class="cluster-breadcrumb" ncy-breadcrumb></span>
<ui-view></ui-view>
<ul class="col-md-3 hidden-sm hidden-xs list-unstyled application-action-col">
<li ng-repeat="clusterAction in clusterController.clusterActions">
<button class="btn btn-link btn-border-less"
ng-disabled="clusterAction.disabled"
ng-hide="clusterAction.hidden"
ng-click="clusterAction.execute()">
<i ng-class="clusterAction.icon"></i> {{ clusterAction.name }}
</button>
</li>
</ul>
</div>

74 changes: 2 additions & 72 deletions src/app/view/endpoints/clusters/cluster/cluster.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,16 @@
'app.utils.utilsService',
'$state',
'$q',
'app.model.modelManager',
'helion.framework.widgets.asyncTaskDialog',
'app.view.endpoints.clusters.cluster.assignUsers'
'app.model.modelManager'
];

function ClusterController($stateParams, $log, utils, $state, $q, modelManager, asyncTaskDialog, assignUsers) {
function ClusterController($stateParams, $log, utils, $state, $q, modelManager) {
var that = this;
var organizationModel = modelManager.retrieve('cloud-foundry.model.organization');
var serviceBindingModel = modelManager.retrieve('cloud-foundry.model.service-binding');
var privateDomains = modelManager.retrieve('cloud-foundry.model.private-domain');
var sharedDomains = modelManager.retrieve('cloud-foundry.model.shared-domain');
var appModel = modelManager.retrieve('cloud-foundry.model.application');
var stackatoInfo = modelManager.retrieve('app.model.stackatoInfo');

this.initialized = false;
this.guid = $stateParams.guid;
Expand All @@ -51,72 +48,6 @@
return utils.getClusterEndpoint(that.userServiceInstanceModel.serviceInstances[that.guid]);
};

this.clusterActions = [
{
name: gettext('Create Organization'),
disabled: true,
execute: function () {
return asyncTaskDialog(
{
title: gettext('Create Organization'),
templateUrl: 'app/view/endpoints/clusters/cluster/detail/actions/create-organization.html',
buttonTitles: {
submit: gettext('Create')
}
},
{
data: {
// Make the form invalid if the name is already taken
organizationNames: organizationModel.organizationNames[that.guid]
}
},
function (orgData) {
if (orgData.name && orgData.name.length > 0) {
return organizationModel.createOrganization(that.guid, orgData.name);
} else {
return $q.reject('Invalid Name!');
}

}
);
},
icon: 'helion-icon-lg helion-icon helion-icon-Tree'
},
{
name: gettext('Create Space'),
disabled: true,
execute: function () {
},
icon: 'helion-icon-lg helion-icon helion-icon-Tree'
},
{
name: gettext('Assign User(s)'),
disabled: true,
execute: function () {
assignUsers.assign({
selectedUsers: {}
});
},
icon: 'helion-icon-lg helion-icon helion-icon-Add_user'
}
];

/**
* Enable actions based on admin status
* N.B. when finer grain ACLs are wired in this should be updated
* */
function enableActions() {
if (stackatoInfo.info.endpoints.hcf[that.guid].user.admin) {
_.forEach(that.clusterActions, function (action) {
action.disabled = false;
});
// Disable these until implemented!
that.clusterActions[1].disabled = true;
}
that.clusterActions[2].disabled =
that.clusterActions[2].disabled || _.keys(organizationModel.organizations).length < 1;
}

function init() {

// Cache all organizations associated with this cluster
Expand All @@ -130,7 +61,6 @@
allDetailsP.push(orgDetailsP);
});
return $q.all(allDetailsP).then(function (val) {
enableActions();
that.organizationNames = organizationModel.organizationNames[that.guid];
return val;
});
Expand Down
5 changes: 5 additions & 0 deletions src/app/view/endpoints/clusters/cluster/cluster.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
text-align: center;
}

.cluster-actions-menu {
position: absolute;
right: 24px; // ok with scroll bar on, no time find a way to satisfy both with and without the scrollbar
}

.cluster-detail {

padding-left: $hpe-unit-space;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
(function () {
'use strict';

angular
.module('app.view.endpoints')
.directive('clusterActions', ClusterActions)
.directive('uniqueSpaceName', UniqueSpaceName);

// Define contextData here so it's available to both directives
var contextData;

ClusterActions.$inject = [];

function ClusterActions() {
return {
restrict: 'E',
bindToController: true,
controller: ClusterActionsController,
controllerAs: 'clusterActionsCtrl',
scope: {
// stateName: '@'
},
templateUrl: 'app/view/endpoints/clusters/cluster/detail/actions/cluster-actions.html'
};
}

ClusterActionsController.$inject = [
'app.model.modelManager',
'$state',
'$q',
'$stateParams',
'app.utils.utilsService',
'helion.framework.widgets.asyncTaskDialog',
'app.view.endpoints.clusters.cluster.assignUsers'
];

/**
* @name OrganizationTileController
* @constructor
* @param {app.model.modelManager} modelManager - the model management service
* @param {object} $state - the angular $state service
* @param {object} $q - the angular $q service
* @param {object} $stateParams - the ui-router $stateParams service
* @param {object} utils - our utils service
* @param {object} asyncTaskDialog - our async dialog service
* @param {object} assignUsersService - service that allows assigning roles to users
* @property {Array} actions - collection of relevant actions that can be executed against cluster
*/
function ClusterActionsController(modelManager, $state, $q, $stateParams, utils, asyncTaskDialog, assignUsersService) {
var that = this;
var stackatoInfo = modelManager.retrieve('app.model.stackatoInfo');
var organizationModel = modelManager.retrieve('cloud-foundry.model.organization');
var spaceModel = modelManager.retrieve('cloud-foundry.model.space');

this.stateName = $state.current.name;
this.clusterGuid = $stateParams.guid;

function getOrgName(org) {
return _.get(org, 'details.org.entity.name');
}

function getExistingSpaceNames(orgGuid) {
var orgSpaces = organizationModel.organizations[that.clusterGuid][orgGuid].spaces;
return _.map(orgSpaces, function (space) {
return space.entity.name;
});
}

var createOrg = {
name: gettext('Create Organization'),
disabled: true,
execute: function () {
return asyncTaskDialog(
{
title: gettext('Create Organization'),
templateUrl: 'app/view/endpoints/clusters/cluster/detail/actions/create-organization.html',
buttonTitles: {
submit: gettext('Create')
}
},
{
data: {
// Make the form invalid if the name is already taken
organizationNames: organizationModel.organizationNames[that.clusterGuid]
}
},
function (orgData) {
if (orgData.name && orgData.name.length > 0) {
return organizationModel.createOrganization(that.clusterGuid, orgData.name);
} else {
return $q.reject('Invalid Name!');
}

}
);
},
icon: 'helion-icon-lg helion-icon helion-icon-Tree'
};

var createSpace = {
name: gettext('Create Space'),
disabled: true,
execute: function () {

var existingSpaceNames, selectedOrg;

// Context-sensitively pre-select the correct organization
if ($stateParams.organization) {
selectedOrg = organizationModel.organizations[that.clusterGuid][$stateParams.organization];
} else {
// Pre-select the most recently created organization
var sortedOrgs = _.sortBy(organizationModel.organizations[that.clusterGuid], function (org) {
return -org.details.created_at;
});
selectedOrg = sortedOrgs[0];
}

existingSpaceNames = getExistingSpaceNames(selectedOrg.details.guid);

function setOrganization() {
contextData.existingSpaceNames = getExistingSpaceNames(contextData.organization.details.guid);
}

function createSpaceDisabled() {
if (contextData.spaces.length >= 10) {
return true;
}
// Make sure all spaces have a valid name before allowing creating another
for (var i = 0; i < contextData.spaces.length; i++) {
var spaceName = contextData.spaces[i];
if (angular.isUndefined(spaceName) || spaceName.length < 1) {
return true;
}
}
return false;
}

function addSpace() {
if (createSpaceDisabled()) {
return;
}
contextData.spaces.push('');
}

function removeSpace() {
contextData.spaces.length--;
}

contextData = {
organization: selectedOrg,
organizations: _.map(organizationModel.organizations[that.clusterGuid], function (org) {
return {
label: getOrgName(org),
value: org
};
}),
existingSpaceNames: existingSpaceNames,
spaces: [''],
setOrganization: setOrganization,
createSpaceDisabled: createSpaceDisabled,
addSpace: addSpace,
removeSpace: removeSpace
};

return asyncTaskDialog(
{
title: gettext('Create Space'),
templateUrl: 'app/view/endpoints/clusters/cluster/detail/actions/create-space.html',
buttonTitles: {
submit: gettext('Create')
}
},
{
data: contextData
},
function () {
var toCreate = [];
for (var i = 0; i < contextData.spaces.length; i++) {
var name = contextData.spaces[i];
if (angular.isDefined(name) && name.length > 0) {
toCreate.push(name);
}
}
if (toCreate.length < 1) {
return $q.reject('Nothing to create!');
}
return spaceModel.createSpaces(that.clusterGuid, contextData.organization.details.guid, toCreate);
}
);
},
icon: 'helion-icon-lg helion-icon helion-icon-Tree'
};

var assignUsers = {
name: gettext('Assign User(s)'),
disabled: true,
execute: function () {
assignUsersService.assign({
selectedUsers: {}
});
},
icon: 'helion-icon-lg helion-icon helion-icon-Add_user'
};

this.clusterActions = [
createOrg,
createSpace,
assignUsers
];

/**
* Enable actions based on admin status
* N.B. when finer grain ACLs are wired in this should be updated
* */
function enableActions() {
if (stackatoInfo.info.endpoints.hcf[that.clusterGuid].user.admin) {
_.forEach(that.clusterActions, function (action) {
action.disabled = false;
});
}
}

function init() {
enableActions();
return $q.resolve();
}

utils.chainStateResolve(this.stateName, $state, init);
}

UniqueSpaceName.$inject = [];

// private validator to ensure there are no duplicates within the list of new names
function UniqueSpaceName() {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
var index = parseInt(attrs.uniqueSpaceName, 10);
ctrl.$validators.dupeName = function (modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
return true;
}
for (var i = 0; i < contextData.spaces.length; i++) {
if (index === i) {
continue;
}
var name = contextData.spaces[i];
if (modelValue === name) {
return false;
}
}
return true;
};
}
};
}
})();
Loading

0 comments on commit 41c5f9b

Please sign in to comment.