Merge "Ability to add and remove subscriptions"

This commit is contained in:
Jenkins 2016-06-20 17:27:36 +00:00 committed by Gerrit Code Review
commit de2fb80dd0
12 changed files with 475 additions and 24 deletions

View File

@ -26,12 +26,19 @@
'horizon.framework.widgets.toast.service'
];
function ZaqarAPI(apiService, toastService) {
function ZaqarAPI(apiService, toast) {
var queuePath = '/api/zaqar/queues/';
var subPath = '/api/zaqar/queues/%s/subscriptions/';
var service = {
getQueues: getQueues,
createQueue: createQueue,
deleteQueue: deleteQueue,
updateQueue: updateQueue
updateQueue: updateQueue,
getSubscriptions: getSubscriptions,
addSubscription: addSubscription,
deleteSubscription: deleteSubscription
};
return service;
@ -39,28 +46,47 @@
//////////
function getQueues() {
return apiService.get('/api/zaqar/queues/')
.error(function() {
toastService.add('error', gettext('Unable to retrieve the Queues.'));
});
var msg = gettext('Unable to retrieve the Queues.');
return apiService.get(queuePath).error(error(msg));
}
function createQueue(newQueue) {
return apiService.put('/api/zaqar/queues/', newQueue)
.error(function() {
toastService.add('error', gettext('Unable to create the queue.'));
});
var msg = gettext('Unable to create the queue.');
return apiService.put(queuePath, newQueue).error(error(msg));
}
function deleteQueue(queueName) {
return apiService.delete('/api/zaqar/queues/', [queueName]);
return apiService.delete(queuePath, [queueName]);
}
function updateQueue(queue) {
return apiService.post('/api/zaqar/queue/' + queue.queue_name, {"metadata": queue.metadata})
.error(function() {
toastService.add('error', gettext('Unable to update the queue.'));
});
var msg = gettext('Unable to update the queue.');
var url = '/api/zaqar/queue/' + queue.queue_name;
var form = { metadata: queue.metadata };
return apiService.post(url, form).error(error(msg));
}
function getSubscriptions(queue) {
var url = interpolate(subPath, [queue.name]);
return apiService.get(url);
}
function addSubscription(sub) {
var msg = gettext('Unable to add subscription.');
var url = interpolate(subPath, [sub.queueName]);
return apiService.put(url, sub).error(error(msg));
}
function deleteSubscription(queue, subscription) {
var msg = gettext('Unable to delete subscription.');
var url = interpolate(subPath, [queue.name]);
return apiService.delete(url, subscription).error(error(msg));
}
function error(message) {
return function() {
toast.add('error', message);
};
}
}
}());

View File

@ -32,6 +32,7 @@
'horizon.dashboard.project.queues.actions.createService',
'horizon.dashboard.project.queues.actions.deleteService',
'horizon.dashboard.project.queues.actions.updateService',
'horizon.dashboard.project.queues.actions.createSubscriptionService',
'horizon.dashboard.project.queues.resourceType'
];
@ -40,6 +41,7 @@
createService,
deleteService,
updateService,
createSubscriptionService,
resourceType
) {
@ -52,6 +54,13 @@
text: gettext('Update')
}
})
.append({
id: 'subscriptionsCreate',
service: createSubscriptionService,
template: {
text: gettext('Create Subscription')
}
})
.append({
id: 'queuesItemDelete',
service: deleteService,

View File

@ -0,0 +1,112 @@
/**
* Copyright 2016 IBM Corp.
*
* 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';
angular
.module('horizon.dashboard.project.queues')
.factory('horizon.dashboard.project.queues.actions.createSubscriptionService',
createSubscriptionService);
createSubscriptionService.$inject = [
'$q',
'horizon.app.core.metadata.service',
'horizon.app.core.openstack-service-api.policy',
'horizon.dashboard.project.queues.events',
'horizon.dashboard.project.queues.actions.createSubscriptionWorkflow',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.framework.widgets.modal.wizard-modal.service',
'horizon.framework.widgets.toast.service'
];
/**
* @ngDoc factory
* @name horizon.dashboard.project.queues.actions.createSubscriptionService
* @Description A service to open the subscriptions wizard.
*/
function createSubscriptionService(
$q, meta, policy, events, createWorkflow, zaqar, wizard, toast) {
var message = {
success: gettext('Subscription %s was successfully created.')
};
var scope;
var model = null;
var service = {
initScope: initScope,
perform: perform,
allowed: allowed
};
return service;
//////////////
// we define initScope so that the table controller
// will know when a new subscription has been created
function initScope($scope) {
scope = $scope;
var subWatcher = $scope.$on(events.SUBSCRIPTION_CHANGED, onSubscriptionChange);
$scope.$on('$destroy', function destroy() {
subWatcher();
});
}
function onSubscriptionChange(e, subscription) {
angular.extend(model, subscription);
e.stopPropagation();
}
function perform(queue) {
model = { subscriber: null, ttl: null, options: {} };
model.queueName = queue.name;
wizard.modal({
scope: scope,
workflow: createWorkflow,
submit: submit
});
}
function allowed(queue) {
return policy.ifAllowed({ rules: [['queue', 'add_subscriptions']] });;
}
function submit() {
return zaqar.addSubscription(model).then(success, error);
}
function success(response) {
angular.extend(model, response.data);
toast.add('success', interpolate(message.success, [model.subscriber]));
scope.$emit(events.SUBSCRIPTION_CREATE_SUCCESS, model);
}
function error(response) {
// TODO: Currently, when server throws an error
// close the modal dialog and display the error message
// In the future, display the error message inside the dialog
// and allow user to continue with workflow
return;
}
} // end of createSubscriptionService
})(); // end of IIFE

View File

@ -0,0 +1,53 @@
/**
* Copyright 2016 IBM Corp.
*
* 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';
angular
.module('horizon.dashboard.project.queues')
.factory('horizon.dashboard.project.queues.actions.createSubscriptionWorkflow',
createSubscriptionWorkflow);
createSubscriptionWorkflow.$inject = [
'horizon.app.core.workflow.factory',
'horizon.dashboard.project.queues.basePath',
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc factory
* @name horizon.dashboard.project.queues.actions.createSubscriptionWorkflow
* @description A workflow for the create subscription action.
*/
function createSubscriptionWorkflow(workflowService, basePath, gettext) {
var workflow = workflowService({
title: gettext('Create Subscription'),
btnText: { finish: gettext('Create') },
steps: [
{
title: gettext('Subscription'),
templateUrl: basePath + 'steps/subscription/subscription.html',
formName: 'subscriptionForm'
}
]
});
return workflow;
}
})();

View File

@ -46,7 +46,8 @@
DETAILS_CHANGED: 'horizon.dashboard.project.queues.DETAILS_CHANGED',
METADATA_CHANGED: 'horizon.dashboard.project.queues.METADATA_CHANGED',
DELETE_SUCCESS: 'horizon.dashboard.project.queues.DELETE_SUCCESS',
UPDATE_SUCCESS: 'horizon.dashboard.project.queues.UPDATE_SUCCESS'
UPDATE_SUCCESS: 'horizon.dashboard.project.queues.UPDATE_SUCCESS',
SUBSCRIPTION_CREATE_SUCCESS: 'horizon.dashboard.project.queues.SUBSCRIPTION_CREATE_SUCCESS'
};
}

View File

@ -0,0 +1,4 @@
.subtitle {
margin: 2em 0;
}

View File

@ -0,0 +1,64 @@
/**
* Copyright 2016 IBM Corp.
*
* 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';
angular
.module('horizon.dashboard.project.queues')
.controller('horizon.dashboard.project.queues.steps.SubscriptionController',
SubscriptionController);
SubscriptionController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.project.queues.events'
];
/**
* @ngdoc controller
* @name horizon.dashboard.project.queues.steps.SubscriptionController
* @description This controller is use for creating a subscription.
*/
function SubscriptionController($scope, zaqar, events) {
var ctrl = this;
ctrl.subscription = {};
ctrl.update = false;
////////////////////////
// watch this object, when it changes, emit to parent listeners
var watcher = $scope.$watchCollection(getSubscription, onSubscriptionChange);
$scope.$on('$destroy', function() {
watcher();
})
////////////////////////
function getSubscription() {
return ctrl.subscription;
}
function onSubscriptionChange(newValue, oldValue){
if (newValue !== oldValue) {
$scope.$emit(events.SUBSCRIPTION_CHANGED, newValue);
}
}
} // end of SubscriptionController
})();

View File

@ -0,0 +1,48 @@
<div ng-controller="horizon.dashboard.project.queues.steps.SubscriptionController as detailsCtrl">
<h1 translate>Subscription</h1>
<div class="content">
<div class="subtitle" translate>
Note that you can create a subscription without defining options.
Subscribers must be in the form of mailto, HTTP, or HTTPS.
The TTL for a subscription must be at least 60 seconds long.
</div>
<div class="selected-source clearfix">
<div class="row">
<div class="col-xs-12 col-sm-8">
<div class="form-group required">
<label class="control-label required">
<translate>Subscriber</translate>
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input required
id="subscriptionForm-subscriber" name="subscriber"
type="text" class="form-control"
ng-model="detailsCtrl.subscription.subscriber"
ng-maxlength="255"
ng-disabled="detailsCtrl.update"
placeholder="{$ 'Enter a subscriber'|translate $}">
</div>
</div>
<div class="col-xs-12 col-sm-8">
<div class="form-group required">
<label class="control-label required" translate>
<translate>Time To Live</translate>
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input required
id="" name="ttl" min="60"
type="number" class="form-control"
ng-model="detailsCtrl.subscription.ttl"
ng-disabled="detailsCtrl.update"
placeholder="{$ 'Enter TTL for subscription'|translate $}">
</div>
</div>
</div><!-- row -->
</div><!-- clearfix -->
</div><!-- content -->
</div><!-- controller -->

View File

@ -0,0 +1,97 @@
/**
* Copyright 2016 IBM Corp.
*
* 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
* @name queuesTableController
* @ngController
*
* @description
* Controller for the queues table
*/
angular
.module('horizon.dashboard.project.queues')
.controller('horizon.dashboard.project.queues.SubscriptionTableController',
SubscriptionTableController);
SubscriptionTableController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.project.queues.events',
'horizon.framework.widgets.toast.service'
];
function SubscriptionTableController($scope, zaqar, events, toast) {
var ctrl = this;
ctrl.queuesMap = {};
ctrl.deleteSubscription = deleteSubscription;
init();
initScope();
//////////
function initScope() {
var expandWatcher = $scope.$on('hzTable:rowExpanded', getSubscriptions);
var createWatcher = $scope.$on(events.SUBSCRIPTION_CREATE_SUCCESS, addSubscription);
$scope.$on('$destroy', function destroy() {
expandWatcher();
createWatcher();
});
}
function init() {}
//////////
function checkAndInitMap(id) {
if (!ctrl.queuesMap.hasOwnProperty(id)) {
ctrl.queuesMap[id] = {
subscriptions: []
};
}
}
function addSubscription(event, sub){
checkAndInitMap(sub.queueName);
ctrl.queuesMap[sub.queueName].subscriptions.push(sub);
}
function deleteSubscription(queue, sub){
var msg = gettext('Removed %(subscriber)s subscriber from the %(queue)s queue.');
var context = { subscriber: sub.subscriber, queue: queue.name };
zaqar.deleteSubscription(queue.name, sub).success(deleteSuccess);
function deleteSuccess(){
toast.add('success', interpolate(msg, context, true));
var index = ctrl.queuesMap[queue.name].subscriptions.indexOf(sub);
if (index >= 0){ ctrl.queuesMap[queue.name].subscriptions.splice(index, 1); }
}
}
function getSubscriptions(event, queue) {
zaqar.getSubscriptions(queue).success(function (response) {
checkAndInitMap(queue.name);
ctrl.queuesMap[queue.name].subscriptions = response;
});
}
}
})();

View File

@ -0,0 +1,28 @@
<table class="table tabler-inner"
ng-controller="horizon.dashboard.project.queues.SubscriptionTableController as subCtrl">
<thead>
<tr>
<th translate>Subscriber</th>
<th translate>Time to Live</th>
<th translate>Options</th>
<th translate>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-if="subCtrl.queuesMap[q.name].subscriptions.length === 0">
<td colspan="100">No subscribers to show.</td>
</tr>
<tr ng-repeat="sub in subCtrl.queuesMap[q.name].subscriptions">
<td>{$ sub.subscriber $}</td>
<td>{$ sub.ttl $}</td>
<td>{$ sub.options $}</td>
<td>
<button class="btn btn-xs btn-danger"
ng-click="subCtrl.deleteSubscription(q, sub)">
<span class="fa fa-trash"></span>
</button>
</td>
</tr>
</tbody>
</table>

View File

@ -30,19 +30,20 @@
queuesTableController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.project.queues.basePath',
'horizon.dashboard.project.queues.events',
'horizon.dashboard.project.queues.resourceType',
'horizon.framework.conf.resource-type-registry.service',
];
function queuesTableController($scope, zaqar, events, type, registry) {
function queuesTableController($scope, zaqar, base, events, type, registry) {
var ctrl = this;
ctrl.queues = [];
ctrl.queuesSrc = [];
ctrl.resourceType = registry.getResourceType(type);
ctrl.subsTemplate = base + 'table/subscriptionTable.html';
init();
initScope();
@ -53,11 +54,13 @@
var createWatcher = $scope.$on(events.CREATE_SUCCESS, onCreateSuccess);
var deleteWatcher = $scope.$on(events.DELETE_SUCCESS, onDeleteSuccess);
var updateWatcher = $scope.$on(events.UPDATE_SUCCESS, onUpdateSuccess);
var subWatcher = $scope.$on(events.SUBSCRIPTION_CREATE_SUCCESS, broadcastEvents);
$scope.$on('$destroy', function destroy() {
createWatcher();
deleteWatcher();
updateWatcher();
})
subWatcher();
});
}
//////////
@ -67,6 +70,12 @@
zaqar.getQueues().then(showQueues);
}
function broadcastEvents(event, data) {
if (event.targetScope !== $scope) {
$scope.$broadcast(event.name, data);
}
}
function showQueues(response) {
// hz-table expects all items to have the id field
// so we need to manually add name as id here

View File

@ -59,7 +59,7 @@
<td class="expander">
<span class="fa fa-chevron-right"
hz-expand-detail duration="200"></span>
hz-expand-detail item="q" duration="200"></span>
</td>
<td class="rsp-p1">{$ q.name $}</td>
@ -71,7 +71,7 @@
Table-row-action-column:
Actions taken here applies to a single item/row.
-->
<actions allowed="table.resourceType.itemActions" type="row" item="q" />
<actions allowed="table.resourceType.itemActions" type="row" item="q"></actions>
</td>
</tr>
<tr ng-repeat-end class="detail-row">
@ -88,8 +88,8 @@
E.g. table header with rsp-p2 should be here with rsp-alt-p2
The layout should minimize vertical space to reduce scrolling.
-->
<div class="row">
<div class="row">
<!-- Important metadata should go here and table header.
It will appear in the table above if there is room, otherwise
it will appear in the drawer when user resizes the window.
@ -100,16 +100,16 @@
<dd>{$ q.free $}</dd>
</dl>
</span>
</div>
<div class="row">
<!-- additional unimportant metadata can be place here -->
<dl class="col-sm-12">
<dt translate>Metadata</dt>
<dd>{$ q.metadata $}</dd>
</dl>
</div>
<ng-include src="table.subsTemplate"></ng-include>
</td>
</tr>