Add create domain action in angular domain panel

This patch adds Create domain action and this patch
is using angular-schema-form.
Other actions will be added in subsequent patches.

To test this patch, it needs to show domain panel and enable angular
feature for domain panel.

To show domain panel (after installing OpenStack with latest Devstack),
modify local_settings.py as follows:

- using backends except signed cookie as SESSION_ENGINE.
  e.g memcached
----
CACHES = {
   'default': {
       'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
       'LOCATION': '127.0.0.1:11211',
   }
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
----

- enable OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT
----
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
----

- set OPENSTACK_KEYSTONE_DEFAULT_DOMAIN to 'Default' domain
----
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'Default'
----

After that, if admin user logged in, the user could see Domain panel and
could operate actions for domains like Create.

To test this patch, after above, set 'domains_panel': True in 'ANGULAR_FEATURES'.

Co-Authored-By: Richard Jones <r1chardj0n3s@gmail.com>
Co-Authored-By: Shu Muto <shu.mutow@gmail.com>

Change-Id: I75d8e566daf451b12933f43ef9ed0b1df1cd24a8
Partially-Implements: blueprint ng-domains
This commit is contained in:
Kenji Ishii 2016-11-14 18:27:22 +09:00 committed by Shu Muto
parent a56765b6b3
commit 73b43115d5
8 changed files with 401 additions and 1 deletions

View File

@ -0,0 +1,54 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @ngname horizon.dashboard.identity.domains.actions
*
* @description
* Provides all of the actions for domains.
*/
angular.module('horizon.dashboard.identity.domains.actions', [
'horizon.dashboard.identity.domains'
])
.run(registerDomainActions);
registerDomainActions.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.identity.domains.actions.create.service',
'horizon.dashboard.identity.domains.resourceType'
];
function registerDomainActions(
registry,
createDomainService,
domainResourceType
) {
var resourceType = registry.getResourceType(domainResourceType);
resourceType.globalActions
.append({
id: 'createDomainAction',
service: createDomainService,
template: {
text: gettext('Create Domain'),
type: 'create'
}
});
}
})();

View File

@ -0,0 +1,24 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function () {
'use strict';
describe('horizon.dashboard.identity.domains.actions', function () {
it('should exist', function () {
expect(angular.module('horizon.dashboard.identity.domains.actions')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,76 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
/**
* @ngdoc factory
* @name horizon.dashboard.identity.domains.actions.create.service
* @description
* Service for the domain create modal
*/
angular
.module('horizon.dashboard.identity.domains.actions')
.factory('horizon.dashboard.identity.domains.actions.create.service', createService);
createService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.keystone',
'horizon.app.core.openstack-service-api.policy',
'horizon.dashboard.identity.domains.actions.workflow.service',
'horizon.dashboard.identity.domains.resourceType',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service'
];
function createService(
$q, keystone, policy, workflow, resourceType, actionResult, modal, toast
) {
var service = {
allowed: allowed,
perform: perform
};
return service;
///////
function allowed() {
return $q.all([
keystone.canEditIdentity('domain'),
policy.ifAllowed({ rules: [['identity', 'create_domain']] })
]);
}
function perform() {
var config = workflow.init();
config.title = gettext("Create Domain");
return modal.open(config).then(submit);
}
function submit(context) {
return keystone.createDomain(context.model).then(success);
}
function success(response) {
var domain = response.data;
var message = gettext('Domain %s was successfully created.');
toast.add('success', interpolate(message, [domain.name]));
return actionResult.getActionResult().created(resourceType, domain.id).result;
}
}
})();

View File

@ -0,0 +1,90 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
describe('horizon.dashboard.identity.domains.actions.create.service', function() {
var service, $scope, $q, modal, keystoneAPI, policyAPI;
var createDomainResponse = {
data: {
id: '1234',
name: 'test',
description: 'desc',
enabled: true
}
};
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.dashboard.identity.domains'));
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
$scope = _$rootScope_.$new();
$q = _$q_;
keystoneAPI = $injector.get('horizon.app.core.openstack-service-api.keystone');
policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy');
modal = $injector.get('horizon.framework.widgets.form.ModalFormService');
service = $injector.get('horizon.dashboard.identity.domains.actions.create.service');
}));
describe('should open the modal', function() {
it('open the modal', function() {
var deferred = $q.defer();
deferred.resolve(true);
spyOn(modal, "open").and.returnValue($q.defer().promise);
service.perform();
$scope.$apply();
expect(modal.open).toHaveBeenCalled();
});
it('should call keystone.createDomain', function() {
var deferred = $q.defer();
deferred.resolve(true);
spyOn(modal, "open").and.returnValue(deferred.promise);
var deferredCreateDomain = $q.defer();
deferredCreateDomain.resolve(createDomainResponse);
spyOn(keystoneAPI, 'createDomain').and.returnValue(deferredCreateDomain.promise);
service.perform();
$scope.$apply();
expect(keystoneAPI.createDomain).toHaveBeenCalled();
});
});
describe('allowed', function() {
it('should allow create domain', function() {
var deferred = $q.defer();
deferred.resolve(true);
spyOn(policyAPI, 'ifAllowed').and.returnValue(deferred.promise);
var deferredCanEdit = $q.defer();
deferredCanEdit.resolve(true);
spyOn(keystoneAPI, 'canEditIdentity').and.returnValue(deferredCanEdit.promise);
var allowed = service.allowed();
expect(allowed).toBeTruthy();
expect(keystoneAPI.canEditIdentity).toHaveBeenCalledWith('domain');
expect(policyAPI.ifAllowed).toHaveBeenCalledWith(
{ rules: [['identity', 'create_domain']] });
});
});
});
})();

View File

@ -0,0 +1 @@
<p translate>Domains provide separation between users and infrastructure used by different organizations.</p>

View File

@ -0,0 +1,112 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
/**
* @ngdoc factory
* @name horizon.dashboard.identity.domains.actions.workflow.service
* @ngController
*
* @description
* Workflow for creating/updating domain
*/
angular
.module('horizon.dashboard.identity.domains.actions')
.factory('horizon.dashboard.identity.domains.actions.workflow.service', DomainWorkflow);
DomainWorkflow.$inject = [
'horizon.dashboard.identity.domains.basePath'
];
function DomainWorkflow(basePath) {
var workflow = {
init: init
};
function init() {
var schema = {
type: "object",
properties: {
name: {
title: gettext('Domain Name'),
type: 'string'
},
description: {
title: gettext('Description'),
type: 'string'
},
enabled: {
title: gettext('Enabled'),
type: 'boolean',
default: true
}
},
required: ['name']
};
var form = [
{
type: 'section',
htmlClass: 'row',
items: [
{
type: 'section',
htmlClass: 'col-sm-6',
items: [
{
key: "name"
},
{
key: "description",
type: "textarea"
},
{
key: "enabled",
type: "radiobuttons",
titleMap: [
{value: true, name: gettext("Yes")},
{value: false, name: gettext("No")}
]
}
]
},
{
type: 'template',
templateUrl: basePath + "actions/workflow/info.help.html"
}
]
}
];
var model = {
name: "",
description: "",
enabled: true
};
var config = {
schema: schema,
form: form,
model: model
};
return config;
}
return workflow;
}
})();

View File

@ -0,0 +1,42 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
describe('horizon.dashboard.identity.domains.actions.workflow.service', function() {
var workflow;
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.dashboard.identity.domains'));
beforeEach(inject(function($injector) {
workflow = $injector.get('horizon.dashboard.identity.domains.actions.workflow.service');
}));
function testInitWorkflow() {
var config = workflow.init();
expect(config.schema).toBeDefined();
expect(config.form).toBeDefined();
expect(config.model).toBeDefined();
return config;
}
it('should be create workflow config for creation', function() {
testInitWorkflow();
});
});
})();

View File

@ -28,7 +28,8 @@
angular
.module('horizon.dashboard.identity.domains', [
'ngRoute',
'horizon.dashboard.identity.domains.details'
'horizon.dashboard.identity.domains.details',
'horizon.dashboard.identity.domains.actions'
])
.constant('horizon.dashboard.identity.domains.resourceType', 'OS::Keystone::Domain')
.run(run)