From 359478432250fa8b3abab271be8edc779987dee1 Mon Sep 17 00:00:00 2001 From: Jacky Hu Date: Wed, 6 Dec 2017 15:36:53 +0800 Subject: [PATCH] Catch up with horizon framework * Use resource registry to reduce boilerplate * Resolve resources before routing * Better organization of the page structure Change-Id: I9fdf0d1bc3389cac0f4cd955dcd76920b7c0f90b Story: 1713855 Task: 5449 Story: 1713854 Task: 5219 Story: 1713853 Task: 5366 --- octavia_dashboard/api/rest/lbaasv2.py | 65 ++++ .../openstack-service-api/lbaasv2.service.js | 56 ++++ .../lbaasv2.service.spec.js | 46 +++ .../actions/create/create.action.service.js | 59 ++-- .../create/create.action.service.spec.js | 83 +---- .../actions/create/wizard.controller.js | 3 +- .../actions/delete/delete.action.service.js | 71 +++-- .../delete/delete.action.service.spec.js | 196 +++++------- .../actions/edit/edit.action.service.js | 43 +-- .../actions/edit/edit.action.service.spec.js | 75 +---- .../actions/row-actions.service.js | 81 ----- .../actions/row-actions.service.spec.js | 51 ---- .../healthmonitors/detail.controller.js | 100 ------ .../healthmonitors/detail.controller.spec.js | 111 ------- .../lbaasv2/healthmonitors/detail.html | 48 --- .../details/detail.controller.js | 104 +++++++ .../details/detail.controller.spec.js | 104 +++++++ .../healthmonitors/details/detail.html | 53 ++++ .../healthmonitors/details/drawer.html | 10 + .../healthmonitors/healthmonitors.module.js | 135 ++++++++- .../healthmonitors.module.spec.js | 43 +++ .../project/lbaasv2/lbaasv2.module.js | 217 ++++++++++++- .../project/lbaasv2/lbaasv2.module.spec.js | 286 ++++++++++++++++-- .../dashboard/project/lbaasv2/lbaasv2.scss | 15 +- .../actions/batch-actions.service.js | 107 ------- .../actions/batch-actions.service.spec.js | 105 ------- .../actions/create/create.service.js | 69 +++++ .../actions/create/create.service.spec.js | 55 ++++ .../actions/delete/delete.action.service.js | 88 +++--- .../delete/delete.action.service.spec.js | 56 ++-- .../listeners/actions/edit/edit.service.js | 69 +++++ .../actions/edit/edit.service.spec.js | 55 ++++ .../listeners/actions/row-actions.service.js | 113 ------- .../actions/row-actions.service.spec.js | 115 ------- .../lbaasv2/listeners/detail.controller.js | 92 ------ .../listeners/detail.controller.spec.js | 104 ------- .../project/lbaasv2/listeners/detail.html | 46 --- .../listeners/details/detail.controller.js | 101 +++++++ .../details/detail.controller.spec.js | 101 +++++++ .../lbaasv2/listeners/details/detail.html | 62 ++++ .../lbaasv2/listeners/details/drawer.html | 9 + .../lbaasv2/listeners/listeners.module.js | 149 ++++++++- .../listeners/listeners.module.spec.js | 43 +++ .../lbaasv2/listeners/table.controller.js | 80 ----- .../listeners/table.controller.spec.js | 97 ------ .../project/lbaasv2/listeners/table.html | 124 -------- .../associate-ip/modal.controller.spec.js | 1 - .../actions/associate-ip/modal.service.js | 5 +- .../associate-ip/modal.service.spec.js | 7 +- .../actions/batch-actions.service.spec.js | 83 ----- .../create.service.js} | 68 ++--- .../actions/create/create.service.spec.js | 56 ++++ .../actions/create/wizard.controller.spec.js | 1 - .../actions/delete/delete.action.service.js | 74 ++--- .../delete/delete.action.service.spec.js | 50 +-- .../actions/disassociate-ip/modal.service.js | 10 +- .../disassociate-ip/modal.service.spec.js | 7 +- .../actions/edit/edit.service.js | 77 +++++ .../actions/edit/edit.service.spec.js | 63 ++++ .../actions/edit/wizard.controller.spec.js | 1 - .../actions/row-actions.service.js | 123 -------- .../actions/row-actions.service.spec.js | 101 ------- .../loadbalancers/detail.controller.js | 92 ------ .../loadbalancers/detail.controller.spec.js | 89 ------ .../project/lbaasv2/loadbalancers/detail.html | 61 ---- .../details/detail.controller.js | 96 ++++++ .../details/detail.controller.spec.js | 99 ++++++ .../lbaasv2/loadbalancers/details/detail.html | 66 ++++ .../lbaasv2/loadbalancers/details/drawer.html | 9 + .../loadbalancers/loadbalancers.module.js | 163 +++++++++- .../loadbalancers.module.spec.js | 45 +++ .../loadbalancers/loadbalancers.service.js | 165 +++++++++- .../loadbalancers.service.spec.js | 196 ++++++++++-- .../project/lbaasv2/loadbalancers/panel.html | 4 + .../lbaasv2/loadbalancers/table.controller.js | 80 ----- .../loadbalancers/table.controller.spec.js | 86 ------ .../project/lbaasv2/loadbalancers/table.html | 155 ---------- .../members/actions/batch-actions.service.js | 73 ----- .../actions/batch-actions.service.spec.js | 67 ---- .../actions/delete/delete.action.service.js | 79 ++--- .../delete/delete.action.service.spec.js | 195 +++++------- .../actions/edit-member/modal.service.js | 43 ++- .../actions/edit-member/modal.service.spec.js | 21 +- .../members/actions/row-actions.service.js | 79 ----- .../actions/row-actions.service.spec.js | 51 ---- .../update-list/update-member-list.service.js | 48 ++- .../update-member-list.service.spec.js | 74 +---- .../lbaasv2/members/detail.controller.js | 108 ------- .../lbaasv2/members/detail.controller.spec.js | 124 -------- .../project/lbaasv2/members/detail.html | 48 --- .../members/details/detail.controller.js | 108 +++++++ .../members/details/detail.controller.spec.js | 105 +++++++ .../lbaasv2/members/details/detail.html | 55 ++++ .../lbaasv2/members/details/drawer.html | 9 + .../project/lbaasv2/members/members.module.js | 146 ++++++++- .../lbaasv2/members/members.module.spec.js | 43 +++ .../lbaasv2/members/table.controller.js | 87 ------ .../lbaasv2/members/table.controller.spec.js | 103 ------- .../project/lbaasv2/members/table.html | 120 -------- .../actions/create/create.action.service.js | 57 ++-- .../create/create.action.service.spec.js | 91 ++---- .../pools/actions/create/wizard.controller.js | 4 +- .../actions/delete/delete.action.service.js | 82 ++--- .../delete/delete.action.service.spec.js | 196 +++++------- .../pools/actions/edit/edit.action.service.js | 48 ++- .../actions/edit/edit.action.service.spec.js | 72 +---- .../pools/actions/row-actions.service.js | 87 ------ .../pools/actions/row-actions.service.spec.js | 52 ---- .../lbaasv2/pools/detail.controller.js | 115 ------- .../lbaasv2/pools/detail.controller.spec.js | 128 -------- .../project/lbaasv2/pools/detail.html | 54 ---- .../pools/details/detail.controller.js | 106 +++++++ .../pools/details/detail.controller.spec.js | 103 +++++++ .../project/lbaasv2/pools/details/detail.html | 69 +++++ .../project/lbaasv2/pools/details/drawer.html | 9 + .../project/lbaasv2/pools/pools.module.js | 153 +++++++++- .../lbaasv2/pools/pools.module.spec.js | 43 +++ .../widgets/detail/detail-status.directive.js | 53 ---- .../detail/detail-status.directive.spec.js | 87 ------ .../lbaasv2/widgets/detail/detail-status.html | 12 - .../members/members.controller.spec.js | 186 +++++------- .../project/lbaasv2/workflow/modal.service.js | 5 +- .../project/lbaasv2/workflow/model.service.js | 2 +- .../lbaasv2/workflow/model.service.spec.js | 2 +- 124 files changed, 4526 insertions(+), 5104 deletions(-) delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/drawer.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/drawer.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.spec.js rename octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/{batch-actions.service.js => create/create.service.js} (54%) create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/drawer.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/panel.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/details/drawer.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/members/table.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/drawer.html delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.spec.js delete mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.html diff --git a/octavia_dashboard/api/rest/lbaasv2.py b/octavia_dashboard/api/rest/lbaasv2.py index 0324b7d0..df65362b 100644 --- a/octavia_dashboard/api/rest/lbaasv2.py +++ b/octavia_dashboard/api/rest/lbaasv2.py @@ -661,6 +661,24 @@ class Pools(generic.View): """ url_regex = r'lbaas/pools/$' + @rest_utils.ajax() + def get(self, request): + """List of pools for the current project. + + The listing result is an object with property "items". + """ + loadbalancer_id = request.GET.get('loadbalancerId') + listener_id = request.GET.get('listenerId') + conn = _get_sdk_connection(request) + pool_list = _sdk_object_to_list(conn.load_balancer.pools( + project_id=request.user.project_id)) + + if loadbalancer_id or listener_id: + pool_list = self._filter_pools(pool_list, + loadbalancer_id, + listener_id) + return {'items': pool_list} + @rest_utils.ajax() def post(self, request): """Create a new pool. @@ -672,6 +690,24 @@ class Pools(generic.View): 'listener_id': request.DATA.get('parentResourceId')} return create_pool(request, **kwargs) + def _filter_pools(self, pool_list, loadbalancer_id, listener_id): + filtered_pools = [] + + for pool in pool_list: + if loadbalancer_id: + if pool['loadbalancers'][0]['id'] == loadbalancer_id: + if listener_id: + if (pool['listeners'] and + pool['listeners'][0]['id'] == listener_id): + filtered_pools.append(pool) + else: + filtered_pools.append(pool) + elif (pool['listeners'] and + pool['listeners'][0]['id'] == listener_id): + filtered_pools.append(pool) + + return filtered_pools + @urls.register class Pool(generic.View): @@ -819,6 +855,26 @@ class HealthMonitors(generic.View): """ url_regex = r'lbaas/healthmonitors/$' + @rest_utils.ajax() + def get(self, request): + """List of health monitors for the current project. + + The listing result is an object with property "items". + """ + pool_id = request.GET.get('poolId') + conn = _get_sdk_connection(request) + health_monitor_list = _sdk_object_to_list( + conn.load_balancer.health_monitors( + project_id=request.user.project_id + ) + ) + + if pool_id: + health_monitor_list = self._filter_health_monitors( + health_monitor_list, + pool_id) + return {'items': health_monitor_list} + @rest_utils.ajax() def post(self, request): """Create a new health monitor. @@ -828,6 +884,15 @@ class HealthMonitors(generic.View): 'pool_id': request.DATA.get('parentResourceId')} return create_health_monitor(request, **kwargs) + def _filter_health_monitors(self, health_monitor_list, pool_id): + filtered_health_monitors = [] + + for health_monitor in health_monitor_list: + if health_monitor['pools'][0]['id'] == pool_id: + filtered_health_monitors.append(health_monitor) + + return filtered_health_monitors + @urls.register class HealthMonitor(generic.View): diff --git a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js index 0fef13c1..6ebd55c4 100644 --- a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js +++ b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js @@ -46,6 +46,7 @@ createListener: createListener, editListener: editListener, deleteListener: deleteListener, + getPools: getPools, getPool: getPool, createPool: createPool, editPool: editPool, @@ -54,6 +55,7 @@ getMember: getMember, deleteMember: deleteMember, editMember: editMember, + getHealthMonitors: getHealthMonitors, getHealthMonitor: getHealthMonitor, deleteHealthMonitor: deleteHealthMonitor, createHealthMonitor: createHealthMonitor, @@ -242,6 +244,38 @@ // Pools + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.getPools + * @description + * Get the list of pools. + * If a loadbalancer ID is passed as a parameter, the returning list of + * pools will be filtered to include only those pools under the + * specified loadbalancer. + * @param {string} loadbalancerId + * Specifies the id of the loadbalancer to request pools for. + * @param {string} listenerId + * Specifies the id of the listener to request pools for. + * + * The listing result is an object with property "items". Each item is + * a pool. + */ + + function getPools(loadbalancerId, listenerId) { + var params = $.extend({}, + { + loadbalancerId: loadbalancerId, + listenerId: listenerId + } + ); + if (!$.isEmptyObject(params)) { + params = { params: params }; + } + return apiService.get('/api/lbaas/pools/', params) + .error(function () { + toastService.add('error', gettext('Unable to retrieve pools.')); + }); + } + /** * @name horizon.app.core.openstack-service-api.lbaasv2.getPool * @description @@ -408,6 +442,28 @@ * Specifies the id of the health monitor. */ + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.getHealthMonitors + * @description + * Get the list of healthmonitors. + * If a pool ID is passed as a parameter, the returning list of + * healthmonitors will be filtered to include only those healthmonitors under the + * specified pool. + * @param {string} id + * Specifies the id of the pool to request healthmonitors for. + * + * The listing result is an object with property "items". Each item is + * a healthmonitor. + */ + + function getHealthMonitors(id) { + var params = id ? {params: {poolId: id}} : {}; + return apiService.get('/api/lbaas/healthmonitors/', params) + .error(function () { + toastService.add('error', gettext('Unable to retrieve health monitors.')); + }); + } + function getHealthMonitor(monitorId) { return apiService.get('/api/lbaas/healthmonitors/' + monitorId + '/') .error(function () { diff --git a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js index fee48608..118b83fd 100644 --- a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js +++ b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js @@ -90,6 +90,37 @@ error: 'Unable to retrieve listener.', testInput: [ '1234', false ] }, + { + func: 'getPools', + method: 'get', + path: '/api/lbaas/pools/', + error: 'Unable to retrieve pools.', + testInput: [ '1234' ], + data: { params: { loadbalancerId: '1234' } } + }, + { + func: 'getPools', + method: 'get', + path: '/api/lbaas/pools/', + error: 'Unable to retrieve pools.', + testInput: [ '1234', '5678' ], + data: { params: { loadbalancerId: '1234', listenerId: '5678' } } + }, + { + func: 'getPools', + method: 'get', + path: '/api/lbaas/pools/', + error: 'Unable to retrieve pools.', + testInput: [ undefined, '5678' ], + data: { params: { listenerId: '5678' } } + }, + { + func: 'getPools', + method: 'get', + path: '/api/lbaas/pools/', + data: {}, + error: 'Unable to retrieve pools.' + }, { func: 'getPool', method: 'get', @@ -142,6 +173,21 @@ data: { weight: 2 }, testInput: [ '1234', '5678', { weight: 2 } ] }, + { + func: 'getHealthMonitors', + method: 'get', + path: '/api/lbaas/healthmonitors/', + error: 'Unable to retrieve health monitors.', + testInput: [ '1234' ], + data: { params: { poolId: '1234' } } + }, + { + func: 'getHealthMonitors', + method: 'get', + path: '/api/lbaas/healthmonitors/', + data: {}, + error: 'Unable to retrieve health monitors.' + }, { func: 'getHealthMonitor', method: 'get', diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.js index 91274031..57f29e57 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,68 +22,46 @@ .factory('horizon.dashboard.project.lbaasv2.healthmonitors.actions.create', createService); createService.$inject = [ - '$q', - '$location', + 'horizon.dashboard.project.lbaasv2.healthmonitors.resourceType', + 'horizon.framework.util.actions.action-result.service', 'horizon.dashboard.project.lbaasv2.workflow.modal', 'horizon.app.core.openstack-service-api.policy', - 'horizon.framework.util.i18n.gettext', - 'horizon.framework.util.q.extensions' + 'horizon.framework.util.i18n.gettext' ]; /** * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.healthmonitors.actions.createService + * * @description * Provides the service for creating a health monitor resource. - * @param $q The angular service for promises. - * @param $location The angular $location service. + * + * @param resourceType The health monitor resource type. + * @param actionResultService The horizon action result service. * @param workflowModal The LBaaS workflow modal service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. - * @param qExtensions Horizon extensions to the $q service. + * * @returns The health monitor create service. */ - function createService($q, $location, workflowModal, policy, gettext, qExtensions) { - var loadbalancerId, listenerId, poolId, statePromise; - - var create = workflowModal.init({ + function createService(resourceType, actionResultService, workflowModal, policy, gettext) { + return workflowModal.init({ controller: 'CreateHealthMonitorWizardController', message: gettext('A new health monitor is being created.'), - handle: onCreate, + handle: handle, allowed: allowed }); - var service = { - init: init, - create: create - }; - - return service; - - ////////////// - - function init(_loadbalancerId_, _listenerId_, _statePromise_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - statePromise = _statePromise_; - return service; + function allowed() { + return policy.ifAllowed({ rules: [['neutron', 'create_health_monitor']] }); } - function allowed(pool) { - poolId = pool.id; - return $q.all([ - statePromise, - qExtensions.booleanAsPromise(!pool.health_monitor_id), - policy.ifAllowed({ rules: [['neutron', 'create_health_monitor']] }) - ]); + function handle(response) { + var newHealthMonitor = response.data; + return actionResultService.getActionResult() + .created(resourceType, newHealthMonitor.id) + .result; } - - function onCreate(response) { - var healthMonitorId = response.data.id; - $location.path('project/load_balancer/' + loadbalancerId + '/listeners/' + - listenerId + '/pools/' + poolId + '/healthmonitors/' + healthMonitorId); - } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.spec.js index 6ea7e956..4d37cf40 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/create.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,89 +18,25 @@ 'use strict'; describe('LBaaS v2 Create Health Monitor Action Service', function() { - var scope, $q, $location, policy, init, service, loadBalancerState; + var policy, service; - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = service.create.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith( - {rules: [['neutron', 'create_health_monitor']]}); - return allowed; - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); - beforeEach(module(function($provide) { - $provide.value('$uibModal', { - open: function() { - return { - result: { - then: function(func) { - func({ data: { id: 'healthmonitor1' } }); - } - } - }; - } - }); - })); - beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - $location = $injector.get('$location'); service = $injector.get('horizon.dashboard.project.lbaasv2.healthmonitors.actions.create'); - init = service.init; - loadBalancerState = $q.defer(); })); - it('should define the correct service properties', function() { - expect(service.init).toBeDefined(); - expect(service.create).toBeDefined(); + it('should check policy to allow creating a health monitor', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed) + .toHaveBeenCalledWith({rules: [['neutron', 'create_health_monitor']]}); }); - it('should have the "allowed" and "perform" functions', function() { - expect(service.create.allowed).toBeDefined(); - expect(service.create.perform).toBeDefined(); - }); - - it('should allow creating a health monitor under an ACTIVE load balancer', function() { - loadBalancerState.resolve(); - init('active', '1', loadBalancerState.promise); - expect(allowed({})).toBe(true); - }); - - it('should not allow creating a health monitor under a NON-ACTIVE load balancer', function() { - loadBalancerState.reject(); - init('non-active', '1', loadBalancerState.promise); - expect(allowed({})).toBe(false); - }); - - it('should not allow creating a health monitor if one already exists', function() { - loadBalancerState.resolve(); - init('active', '1', loadBalancerState.promise); - expect(allowed({ health_monitor_id: '1234' })).toBe(false); - }); - - it('should redirect after create', function() { - loadBalancerState.resolve(); - spyOn($location, 'path').and.callThrough(); - init('loadbalancer1', 'listener1', loadBalancerState.promise).create.allowed({id: 'pool1'}); - service.create.perform(); - expect($location.path).toHaveBeenCalledWith( - 'project/load_balancer/loadbalancer1/listeners/listener1/pools/pool1/' + - 'healthmonitors/healthmonitor1'); + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/wizard.controller.js index 8356fe1e..644aa4c7 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/wizard.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/create/wizard.controller.js @@ -32,6 +32,7 @@ $scope, $routeParams, model, workflowService, gettext ) { var loadbalancerId = $routeParams.loadbalancerId; + var poolId = $routeParams.poolId; var scope = $scope; scope.model = model; scope.submit = scope.model.submit; @@ -40,7 +41,7 @@ 'fa fa-cloud-download', ['monitor'] ); - scope.model.initialize('monitor', false, loadbalancerId, scope.launchContext.id); + scope.model.initialize('monitor', false, loadbalancerId, poolId); } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.js index 6e56543b..2fcdfb75 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,9 +22,9 @@ .factory('horizon.dashboard.project.lbaasv2.healthmonitors.actions.delete', deleteService); deleteService.$inject = [ - '$q', + 'horizon.dashboard.project.lbaasv2.healthmonitors.resourceType', + 'horizon.framework.util.actions.action-result.service', '$location', - '$route', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.policy', @@ -33,24 +34,27 @@ /** * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.healthmonitors.actions.deleteService + * * @description * Brings up the delete health monitor confirmation modal dialog. * On submit, deletes selected health monitor. * On cancel, does nothing. - * @param $q The angular service for promises. + * + * @param resourceType The health monitor resource type. + * @param actionResultService The horizon action result service. * @param $location The angular $location service. - * @param $route The angular $route service. * @param deleteModal The horizon delete modal service. * @param api The LBaaS v2 API service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. + * * @returns The health monitor delete service. */ function deleteService( - $q, $location, $route, deleteModal, api, policy, gettext + resourceType, actionResultService, $location, deleteModal, api, policy, gettext ) { - var loadbalancerId, listenerId, poolId, statePromise; + var loadbalancerId, listenerId, poolId; var context = { labels: { title: gettext('Confirm Delete Health Monitor'), @@ -68,50 +72,51 @@ var service = { perform: perform, allowed: allowed, - init: init + deleteResult: deleteResult // exposed just for testing }; return service; ////////////// - function init(_loadbalancerId_, _listenerId_, _poolId_, _statePromise_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - poolId = _poolId_; - statePromise = _statePromise_; - return service; - } - - function perform(item) { - deleteModal.open({ $emit: actionComplete }, [item], context); - } - function allowed(/*item*/) { - return $q.all([ - statePromise, - // This rule is made up and should therefore always pass. I assume at some point there - // will be a valid rule similar to this that we will want to use. - policy.ifAllowed({ rules: [['neutron', 'delete_health_monitor']] }) - ]); + // This rule is made up and should therefore always pass. I assume at some point there + // will be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'delete_health_monitor']] }); } - function deleteItem(id) { - return api.deleteHealthMonitor(id, true); + function perform(items, scope) { + var healthMonitors = angular.isArray(items) ? items : [items]; + healthMonitors.map(function(item) { + loadbalancerId = item.loadbalancerId; + listenerId = item.listenerId; + poolId = item.poolId; + }); + return deleteModal.open(scope, healthMonitors, context).then(deleteResult); } - function actionComplete(eventType) { - if (eventType === context.failedEvent) { - // Error, reload page - $route.reload(); - } else { - // Success, go back to pool details page + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { var path = 'project/load_balancer/' + loadbalancerId + '/listeners/' + listenerId + '/pools/' + poolId; $location.path(path); } + return actionResult.result; } + function deleteItem(id) { + return api.deleteHealthMonitor(id, true); + } } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.spec.js index 70a17f1c..1e2d3613 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/delete/delete.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,140 +18,87 @@ 'use strict'; describe('LBaaS v2 Health Monitor Delete Service', function() { - var service, policy, modal, lbaasv2Api, $scope, $location, $q, toast, monitor; - - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(makePromise()); - var promise = service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - $scope.$apply(); - expect(policy.ifAllowed) - .toHaveBeenCalledWith({rules: [['neutron', 'delete_health_monitor']]}); - return allowed; - } - - function makePromise(reject) { - var def = $q.defer(); - def[reject ? 'reject' : 'resolve'](); - return def.promise; - } - - function isActionable(id) { - if (id === 'active') { - return $q.when(); - } else { - return $q.reject(); - } - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); + beforeEach(module('horizon.app.core')); beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module('horizon.framework')); - beforeEach(function() { - monitor = { id: '1', name: 'HealthMonitor1' }; - }); + var deleteModalService, service, lbaasv2API, policyAPI, $location; - beforeEach(module(function($provide) { - $provide.value('$uibModal', { - open: function() { - return { - result: makePromise() - }; - } - }); - $provide.value('horizon.app.core.openstack-service-api.lbaasv2', { - deleteHealthMonitor: function() { - return makePromise(); - } - }); - $provide.value('$location', { - path: function() { - return ''; - } - }); - })); - - beforeEach(inject(function ($injector) { - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - lbaasv2Api = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - modal = $injector.get('horizon.framework.widgets.modal.deleteModalService'); - $scope = $injector.get('$rootScope').$new(); - $location = $injector.get('$location'); - $q = $injector.get('$q'); - toast = $injector.get('horizon.framework.widgets.toast.service'); + beforeEach(inject(function($injector) { service = $injector.get('horizon.dashboard.project.lbaasv2.healthmonitors.actions.delete'); - service.init('1', '2', '3', isActionable('active')); - $scope.$apply(); + lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + deleteModalService = $injector.get('horizon.framework.widgets.modal.deleteModalService'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + $location = $injector.get('$location'); })); - it('should have the "allowed" and "perform" functions', function() { - expect(service.allowed).toBeDefined(); - expect(service.perform).toBeDefined(); + describe('perform method', function() { + beforeEach(function () { + // just need for this to return something that looks like a promise but does nothing + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + }); + + it('should open the modal with correct label', function () { + service.perform({name: 'spam'}); + var labels = deleteModalService.open.calls.argsFor(0)[2].labels; + expect(deleteModalService.open).toHaveBeenCalled(); + angular.forEach(labels, function eachLabel(label) { + expect(label.toLowerCase()).toContain('health monitor'); + }); + }); + + it('should open the delete modal with correct entities', function () { + service.perform([{name: 'one'}, {name: 'two'}]); + var entities = deleteModalService.open.calls.argsFor(0)[1]; + expect(deleteModalService.open).toHaveBeenCalled(); + expect(entities.length).toEqual(2); + }); + + it('should pass in a function that deletes a health monitor', function () { + spyOn(lbaasv2API, 'deleteHealthMonitor').and.callFake(angular.noop); + service.perform({id: 1, name: 'one'}); + var contextArg = deleteModalService.open.calls.argsFor(0)[2]; + var deleteFunction = contextArg.deleteEntity; + deleteFunction(1); + expect(lbaasv2API.deleteHealthMonitor).toHaveBeenCalledWith(1, true); + }); }); - it('should allow deleting health monitor from load balancer in ACTIVE state', function() { - expect(allowed()).toBe(true); - }); - - it('should not allow deleting health monitor from load balancer in PENDING state', function() { - service.init('1', '2', '3', isActionable('pending')); - expect(allowed()).toBe(false); - }); - - it('should open the delete modal', function() { - spyOn(modal, 'open'); - service.perform(monitor); - $scope.$apply(); - expect(modal.open.calls.count()).toBe(1); - var args = modal.open.calls.argsFor(0); - expect(args.length).toBe(3); - expect(args[0]).toEqual({ $emit: jasmine.any(Function) }); - expect(args[1]).toEqual([monitor]); - expect(args[2]).toEqual(jasmine.objectContaining({ - labels: jasmine.any(Object), - deleteEntity: jasmine.any(Function) - })); - expect(args[2].labels.title).toBe('Confirm Delete Health Monitor'); - }); - - it('should pass function to modal that deletes the health monitor', function() { - spyOn(modal, 'open').and.callThrough(); - spyOn(lbaasv2Api, 'deleteHealthMonitor').and.callThrough(); - service.perform(monitor); - $scope.$apply(); - expect(lbaasv2Api.deleteHealthMonitor.calls.count()).toBe(1); - expect(lbaasv2Api.deleteHealthMonitor).toHaveBeenCalledWith('1', true); - }); - - it('should show message if any items fail to be deleted', function() { - spyOn(modal, 'open').and.callThrough(); - spyOn(lbaasv2Api, 'deleteHealthMonitor').and.returnValue(makePromise(true)); - spyOn(toast, 'add'); - service.perform(monitor); - $scope.$apply(); - expect(modal.open).toHaveBeenCalled(); - expect(lbaasv2Api.deleteHealthMonitor.calls.count()).toBe(1); - expect(toast.add).toHaveBeenCalledWith('error', 'The following health monitor could not ' + - 'be deleted: HealthMonitor1.'); - }); - - it('should return to pool details after delete', function() { - var path = 'project/load_balancer/1/listeners/2/pools/3'; + it('should handle the action result properly', function() { spyOn($location, 'path'); - spyOn(toast, 'add'); - service.perform(monitor); - $scope.$apply(); + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + spyOn(lbaasv2API, 'deleteHealthMonitor').and.callFake(angular.noop); + service.perform({loadbalancerId: 1, listenerId: 2, poolId: 3, id: 1, name: 'one'}); + var result = service.deleteResult({ + fail: [], + pass: [{ + context: { + id: 1 + } + }] + }); + var path = 'project/load_balancer/1/listeners/2/pools/3'; expect($location.path).toHaveBeenCalledWith(path); - expect(toast.add).toHaveBeenCalledWith('success', 'Deleted health monitor: HealthMonitor1.'); + expect(result.deleted[0].id).toBe(1); + result = service.deleteResult({ + pass: [], + fail: [{ + context: { + id: 1 + } + }] + }); + expect(result.failed[0].id).toBe(1); }); - }); + describe('allow method', function() { + it('should use default policy if batch action', function () { + spyOn(policyAPI, 'ifAllowed'); + service.allowed(); + expect(policyAPI.ifAllowed).toHaveBeenCalled(); + }); + }); // end of allowed + + }); // end of delete + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.js index baf61537..7020a04f 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,8 +22,8 @@ .factory('horizon.dashboard.project.lbaasv2.healthmonitors.actions.edit', editService); editService.$inject = [ - '$q', - '$route', + 'horizon.dashboard.project.lbaasv2.healthmonitors.resourceType', + 'horizon.framework.util.actions.action-result.service', 'horizon.dashboard.project.lbaasv2.workflow.modal', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.i18n.gettext' @@ -31,50 +32,36 @@ /** * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.healthmonitors.actions.editService + * * @description * Provides the service for editing a health monitor resource. - * @param $q The angular service for promises. - * @param $route The angular $route service. + * + * @param resourceType The health monitor resource type. + * @param actionResultService The horizon action result service. * @param workflowModal The LBaaS workflow modal service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. + * * @returns The health monitor edit service. */ - function editService($q, $route, workflowModal, policy, gettext) { - var statePromise; + function editService(resourceType, actionResultService, workflowModal, policy, gettext) { - var edit = workflowModal.init({ + return workflowModal.init({ controller: 'EditHealthMonitorWizardController', message: gettext('The health monitor has been updated.'), handle: handle, allowed: allowed }); - var service = { - init: init, - edit: edit - }; - - return service; - - ////////////// - - function init(_statePromise_) { - statePromise = _statePromise_; - return service; - } - function allowed(/*healthmonitor*/) { - return $q.all([ - statePromise, - policy.ifAllowed({ rules: [['neutron', 'update_health_monitor']] }) - ]); + return policy.ifAllowed({ rules: [['neutron', 'update_health_monitor']] }); } - function handle(/*response*/) { - $route.reload(); + function handle(response) { + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.monitor.id) + .result; } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.spec.js index 98522853..08cea7ce 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/edit/edit.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,81 +18,25 @@ 'use strict'; describe('LBaaS v2 Edit Health Monitor Action Service', function() { - var scope, $q, $route, policy, init, service, loadBalancerState; + var policy, service; - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = service.edit.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith( - {rules: [['neutron', 'update_health_monitor']]}); - return allowed; - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); - beforeEach(module(function($provide) { - $provide.value('$uibModal', { - open: function() { - return { - result: { - then: function(func) { - func({ data: { id: 'healthmonitor1' } }); - } - } - }; - } - }); - })); - beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - $route = $injector.get('$route'); service = $injector.get('horizon.dashboard.project.lbaasv2.healthmonitors.actions.edit'); - init = service.init; - loadBalancerState = $q.defer(); })); - it('should define the correct service properties', function() { - expect(service.init).toBeDefined(); - expect(service.edit).toBeDefined(); + it('should check policy to allow editing a health monitor', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed) + .toHaveBeenCalledWith({rules: [['neutron', 'update_health_monitor']]}); }); - it('should have the "allowed" and "perform" functions', function() { - expect(service.edit.allowed).toBeDefined(); - expect(service.edit.perform).toBeDefined(); - }); - - it('should allow edit a health monitor under an ACTIVE load balancer', function() { - loadBalancerState.resolve(); - init(loadBalancerState.promise); - expect(allowed({})).toBe(true); - }); - - it('should not allow editing a health monitor under a NON-ACTIVE load balancer', function() { - loadBalancerState.reject(); - init(loadBalancerState.promise); - expect(allowed({})).toBe(false); - }); - - it('should reload page after edit', function() { - loadBalancerState.resolve(); - spyOn($route, 'reload'); - init(loadBalancerState.promise).edit.allowed({}); - service.edit.perform(); - expect($route.reload).toHaveBeenCalled(); + it('should handle the action result properly', function() { + var result = service.handle({config: {data: {monitor: {id: 1}}}}); + expect(result.updated[0].id).toBe(1); }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.js deleted file mode 100644 index 9bc4394a..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.lbaasv2.healthmonitors') - .factory('horizon.dashboard.project.lbaasv2.healthmonitors.actions.rowActions', - rowActions); - - rowActions.$inject = [ - 'horizon.framework.util.i18n.gettext', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.edit', - 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.delete' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.healthmonitors.actions.rowActions - * - * @description - * Provides the service for the health monitor row actions. - * - * @param gettext The horizon gettext function for translation. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param editService The LBaaS v2 health monitor edit service. - * @param deleteService The LBaaS v2 health monitor delete service. - * @returns Health monitor row actions service object. - */ - - function rowActions(gettext, loadBalancersService, editService, deleteService) { - var loadBalancerIsActionable, loadbalancerId, listenerId, poolId; - - var service = { - actions: actions, - init: init - }; - - return service; - - /////////////// - - function init(_loadbalancerId_, _listenerId_, _poolId_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - poolId = _poolId_; - loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId); - return service; - } - - function actions() { - return [{ - service: editService.init(loadBalancerIsActionable).edit, - template: { - text: gettext('Edit') - } - },{ - service: deleteService.init(loadbalancerId, listenerId, poolId, loadBalancerIsActionable), - template: { - text: gettext('Delete Health Monitor'), - type: 'delete' - } - }]; - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.spec.js deleted file mode 100644 index 31abe592..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/actions/row-actions.service.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Health Monitor Row Actions Service', function() { - var actions; - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(inject(function ($injector) { - var rowActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.rowActions'); - actions = rowActionsService.init('1', '2', '3').actions(); - var loadbalancerService = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.service'); - spyOn(loadbalancerService, 'isActionable').and.returnValue(true); - })); - - it('should define correct table row actions', function() { - expect(actions.length).toBe(2); - expect(actions[0].template.text).toBe('Edit'); - expect(actions[1].template.text).toBe('Delete Health Monitor'); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.js deleted file mode 100644 index 908b4618..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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.lbaasv2.healthmonitors') - .controller('HealthMonitorDetailController', HealthMonitorDetailController); - - HealthMonitorDetailController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.rowActions', - '$routeParams', - '$q' - ]; - - /** - * @ngdoc controller - * @name HealthMonitorDetailController - * - * @description - * Controller for the LBaaS v2 health monitor detail page. - * - * @param api The LBaaS v2 API service. - * @param rowActions The LBaaS v2 health monitor row actions service. - * @param $routeParams The angular $routeParams service. - * @param $q The angular service for promises. - * @returns undefined - */ - - function HealthMonitorDetailController(api, rowActions, $routeParams, $q) { - var ctrl = this; - - ctrl.loading = true; - ctrl.error = false; - ctrl.actions = rowActions.init($routeParams.loadbalancerId, - $routeParams.listenerId, - $routeParams.poolId).actions; - - init(); - - //////////////////////////////// - - function init() { - ctrl.healthmonitor = null; - ctrl.pool = null; - ctrl.listener = null; - ctrl.loadbalancer = null; - ctrl.loading = true; - ctrl.error = false; - $q.all([ - api.getHealthMonitor($routeParams.healthmonitorId) - .then(success('healthmonitor'), fail('healthmonitor')), - api.getPool($routeParams.poolId) - .then(success('pool'), fail('pool')), - api.getListener($routeParams.listenerId) - .then(success('listener'), fail('listener')), - api.getLoadBalancer($routeParams.loadbalancerId) - .then(success('loadbalancer'), fail('loadbalancer')) - ]).then(postInit, initError); - } - - function success(property) { - return angular.bind(null, function setProp(property, response) { - ctrl[property] = response.data; - }, property); - } - - function fail(property) { - return angular.bind(null, function setProp(property, error) { - ctrl[property] = null; - throw error; - }, property); - } - - function postInit() { - ctrl.loading = false; - } - - function initError() { - ctrl.loading = false; - ctrl.error = true; - } - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.spec.js deleted file mode 100644 index fd02239d..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.controller.spec.js +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Healthmonitor Detail Controller', function() { - var lbaasv2API, $controller, apiFail, qAllFail; - - function fakePromise(data, reject) { - return { - then: function(success, fail) { - if (reject) { - fail(); - } else { - success({ data: data }); - } - return fakePromise(); - } - }; - } - - function fakeAPI() { - return fakePromise('foo', apiFail); - } - - function loadbalancerAPI() { - return fakePromise({ provisioning_status: 'ACTIVE' }); - } - - function qAll() { - return fakePromise(null, qAllFail); - } - - function createController() { - return $controller('HealthMonitorDetailController', { - $routeParams: { - loadbalancerId: 'loadbalancerId', - listenerId: 'listenerId', - poolId: 'poolId', - healthmonitorId: 'healthmonitorId' - } - }); - } - - /////////////////////// - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - apiFail = false; - qAllFail = false; - - $provide.value('$q', { all: qAll }); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - spyOn(lbaasv2API, 'getHealthMonitor').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getPool').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); - $controller = $injector.get('$controller'); - })); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getHealthMonitor).toHaveBeenCalledWith('healthmonitorId'); - expect(lbaasv2API.getPool).toHaveBeenCalledWith('poolId'); - expect(lbaasv2API.getListener).toHaveBeenCalledWith('listenerId'); - expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('loadbalancerId'); - expect(ctrl.loadbalancer).toEqual({ provisioning_status: 'ACTIVE' }); - expect(ctrl.listener).toBe('foo'); - expect(ctrl.pool).toBe('foo'); - expect(ctrl.healthmonitor).toBe('foo'); - }); - - it('should throw error on API fail', function() { - apiFail = true; - var init = function() { - createController(); - }; - expect(init).toThrow(); - }); - - it('should set error state if any APIs fail', function() { - qAllFail = true; - var ctrl = createController(); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(true); - }); - - }); - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.html deleted file mode 100644 index a199a712..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/detail.html +++ /dev/null @@ -1,48 +0,0 @@ -
- -
- -
-
-
-
Type
-
{$ ::ctrl.healthmonitor.type $}
-
Delay
-
{$ ::ctrl.healthmonitor.delay $}
-
Max Retries
-
{$ ::ctrl.healthmonitor.max_retries $}
-
Max Retries Down
-
{$ ::ctrl.healthmonitor.max_retries_down $}
-
Timeout
-
{$ ::ctrl.healthmonitor.timeout $}
-
HTTP Method
-
{$ ::ctrl.healthmonitor.http_method $}
-
Expected Codes
-
{$ ::ctrl.healthmonitor.expected_codes $}
-
URL Path
-
{$ ::ctrl.healthmonitor.url_path $}
-
Admin State Up
-
{$ ctrl.healthmonitor.admin_state_up | yesno $}
-
Monitor ID
-
{$ ::ctrl.healthmonitor.id $}
-
Project ID
-
{$ ::ctrl.healthmonitor.project_id $}
-
Created At
-
{$ ::ctrl.healthmonitor.created_at $}
-
Updated At
-
{$ ::ctrl.healthmonitor.updated_at $}
-
-
-
-
-
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js new file mode 100644 index 00000000..ed45dd3a --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js @@ -0,0 +1,104 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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.lbaasv2.healthmonitors') + .controller('HealthMonitorDetailController', HealthMonitorDetailController); + + HealthMonitorDetailController.$inject = [ + 'loadbalancer', + 'listener', + 'pool', + 'healthmonitor', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.healthmonitors.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name HealthMonitorDetailController + * + * @description + * Controller for the LBaaS v2 health monitor detail page. + * + * @param loadbalancer The loadbalancer object. + * @param listener The listener object. + * @param pool The pool object. + * @param healthmonitor The health monitor object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The health monitor resource type. + * @param typeRegistry The horizon resource type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function HealthMonitorDetailController( + loadbalancer, listener, pool, healthmonitor, loadBalancersService, + resourceType, typeRegistry, spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.loadbalancer = loadbalancer; + ctrl.listener = listener; + ctrl.pool = pool; + ctrl.healthmonitor = healthmonitor; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.identifier = ctrl.resourceType.parsePath(ctrl.healthmonitor.id); + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.healthmonitor = response.data; + ctrl.healthmonitor.loadbalancerId = ctrl.loadbalancer.id; + ctrl.healthmonitor.listenerId = ctrl.listener.id; + ctrl.healthmonitor.poolId = ctrl.pool.id; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); + ctrl.context.loadPromise.then(loadData); + } + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js new file mode 100644 index 00000000..a4da6718 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js @@ -0,0 +1,104 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 HealthMonitor Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('HealthMonitorDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + listener: { id: '123' }, + pool: { id: '123' }, + healthmonitor: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + expect(ctrl.listener).toBeDefined(); + expect(ctrl.pool).toBeDefined(); + expect(ctrl.healthmonitor).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing'}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + }); + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.html new file mode 100644 index 00000000..81df2083 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.html @@ -0,0 +1,53 @@ + +
+
+ + +
+
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/drawer.html b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/drawer.html new file mode 100644 index 00000000..986478df --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/drawer.html @@ -0,0 +1,10 @@ + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.js index 0a7d50fc..eb44d023 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -26,6 +27,138 @@ */ angular - .module('horizon.dashboard.project.lbaasv2.healthmonitors', []); + .module('horizon.dashboard.project.lbaasv2.healthmonitors', []) + .constant('horizon.dashboard.project.lbaasv2.healthmonitors.resourceType', + 'OS::Octavia::HealthMonitor') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.create', + 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.edit', + 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.delete', + 'horizon.dashboard.project.lbaasv2.healthmonitors.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + deleteService, + resourceType + ) { + var healthMonitorResourceType = registry.getResourceType(resourceType); + + healthMonitorResourceType + .setNames(gettext('Health Monitor'), gettext('Health Monitors')) + .setSummaryTemplateUrl(basePath + 'healthmonitors/details/drawer.html') + .setProperties(healthMonitorProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getHealthMonitorsPromise) + .setLoadFunction(loadBalancerService.getHealthMonitorPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + urlFunction: loadBalancerService.getHealthMonitorDetailsPath + }) + .append({ + id: 'type', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + healthMonitorResourceType.itemActions + .append({ + id: 'healthMonitorEdit', + service: editService, + template: { + text: gettext('Edit Health Monitor') + } + }) + .append({ + id: 'healthMonitorDelete', + service: deleteService, + template: { + text: gettext('Delete Health Monitor'), + type: 'delete' + } + }); + + healthMonitorResourceType.globalActions + .append({ + id: 'healthMonitorCreate', + service: createService, + template: { + type: 'create', + text: gettext('Create Health Monitor') + } + }); + + healthMonitorResourceType.batchActions + .append({ + id: 'healthMonitorBatchDelete', + service: deleteService, + template: { + text: gettext('Delete Health Monitors'), + type: 'delete-selected' + } + }); + } + + function healthMonitorProperties(loadBalancerService) { + return { + id: gettext('ID'), + name: { + label: gettext('Name'), + filters: ['noName'] + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + type: gettext('Type'), + delay: gettext('Delay'), + timeout: gettext('Timeout'), + max_retries: gettext('Max Retries'), + max_retries_down: gettext('Max Retries Down'), + http_method: gettext('HTTP Method'), + url_path: gettext('URL Path'), + expected_codes: gettext('Expected Codes'), + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + }, + pools: gettext('Pools') + }; + } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.spec.js index 63198d6a..d9fc0a6d 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/healthmonitors.module.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -22,4 +23,46 @@ }); }); + describe('LBaaS v2 Healthmonitors Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.healthmonitors.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'healthMonitorEdit')).toBe(true); + expect(actionHasId(actions, 'healthMonitorDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'healthMonitorCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'healthMonitorBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js index 4026802f..83de477c 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js @@ -1,5 +1,6 @@ /* * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -25,12 +26,15 @@ angular .module('horizon.dashboard.project.lbaasv2', [ - 'ngRoute', 'horizon.dashboard.project.lbaasv2.loadbalancers', 'horizon.dashboard.project.lbaasv2.listeners', 'horizon.dashboard.project.lbaasv2.pools', 'horizon.dashboard.project.lbaasv2.members', - 'horizon.dashboard.project.lbaasv2.healthmonitors' + 'horizon.dashboard.project.lbaasv2.healthmonitors', + 'horizon.framework.conf', + 'horizon.framework.widgets', + 'horizon.framework.util', + 'horizon.app.core' ]) .config(config) .constant('horizon.dashboard.project.lbaasv2.patterns', { @@ -45,7 +49,12 @@ }) .constant('horizon.dashboard.project.lbaasv2.popovers', { ipAddresses: '' - }); + }) + .run(['$rootScope', '$location', function ($rootScope, $location) { + $rootScope.$on('$routeChangeError', function() { + $location.path('project/load_balancer'); + }); + }]); config.$inject = [ '$provide', @@ -65,22 +74,212 @@ $routeProvider .when(loadbalancers, { - templateUrl: basePath + 'loadbalancers/table.html' + templateUrl: basePath + 'loadbalancers/panel.html' }) .when(loadbalancers + '/:loadbalancerId', { - templateUrl: basePath + 'loadbalancers/detail.html' + templateUrl: basePath + 'loadbalancers/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ] + }, + controller: 'LoadBalancerDetailController', + controllerAs: 'ctrl' }) .when(listener, { - templateUrl: basePath + 'listeners/detail.html' + templateUrl: basePath + 'listeners/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ], + listener: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getListener($route.current.params.listenerId).then( + function success(response) { + response.data.loadbalancerId = $route.current.params.loadbalancerId; + return response.data; + } + ); + } + ] + }, + controller: 'ListenerDetailController', + controllerAs: 'ctrl' }) .when(pool, { - templateUrl: basePath + 'pools/detail.html' + templateUrl: basePath + 'pools/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ], + listener: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getListener($route.current.params.listenerId).then( + function success(response) { + return response.data; + } + ); + } + ], + pool: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getPool($route.current.params.poolId).then( + function success(response) { + response.data.loadbalancerId = $route.current.params.loadbalancerId; + response.data.listenerId = $route.current.params.listenerId; + return response.data; + } + ); + } + ] + }, + controller: 'PoolDetailController', + controllerAs: 'ctrl' }) .when(member, { - templateUrl: basePath + 'members/detail.html' + templateUrl: basePath + 'members/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ], + listener: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getListener($route.current.params.listenerId).then( + function success(response) { + return response.data; + } + ); + } + ], + pool: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getPool($route.current.params.poolId).then( + function success(response) { + return response.data; + } + ); + } + ], + member: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getMember($route.current.params.poolId, + $route.current.params.memberId).then( + function success(response) { + response.data.loadbalancerId = $route.current.params.loadbalancerId; + response.data.listenerId = $route.current.params.listenerId; + response.data.poolId = $route.current.params.poolId; + return response.data; + } + ); + } + ] + }, + controller: 'MemberDetailController', + controllerAs: 'ctrl' }) .when(healthmonitor, { - templateUrl: basePath + 'healthmonitors/detail.html' + templateUrl: basePath + 'healthmonitors/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ], + listener: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getListener($route.current.params.listenerId).then( + function success(response) { + return response.data; + } + ); + } + ], + pool: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getPool($route.current.params.poolId).then( + function success(response) { + return response.data; + } + ); + } + ], + healthmonitor: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getHealthMonitor( + $route.current.params.healthmonitorId).then( + function success(response) { + response.data.loadbalancerId = $route.current.params.loadbalancerId; + response.data.listenerId = $route.current.params.listenerId; + response.data.poolId = $route.current.params.poolId; + return response.data; + } + ); + } + ] + }, + controller: 'HealthMonitorDetailController', + controllerAs: 'ctrl' }); } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js index 5cbb7bec..86f8149a 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -104,43 +105,270 @@ inject(); }); - it('should route URLs', function () { + it('should route to loadbalancer panel', function () { var loadbalancers = '/project/load_balancer'; - var listener = loadbalancers + '/:loadbalancerId/listeners/:listenerId'; - var pool = listener + '/pools/:poolId'; - var member = pool + '/members/:memberId'; - var healthmonitor = pool + '/healthmonitors/:healthmonitorId'; var routes = [[ loadbalancers, { - templateUrl: basePath + 'loadbalancers/table.html' - } - ], [ - loadbalancers + '/:loadbalancerId', { - templateUrl: basePath + 'loadbalancers/detail.html' - } - ], [ - listener, { - templateUrl: basePath + 'listeners/detail.html' - } - ], [ - pool, { - templateUrl: basePath + 'pools/detail.html' - } - ], [ - member, { - templateUrl: basePath + 'members/detail.html' - } - ], [ - healthmonitor, { - templateUrl: basePath + 'healthmonitors/detail.html' + templateUrl: basePath + 'loadbalancers/panel.html' } ]]; - expect($routeProvider.when.calls.count()).toBe(6); - angular.forEach($routeProvider.when.calls.all(), function(call, i) { - expect(call.args).toEqual(routes[i]); + routes.forEach(function(route) { + expect($routeProvider.when).toHaveBeenCalledWith(route[0], route[1]); }); }); + + it('should route resolved loadbalancer detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/loadbalancers/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + + }); + })); + + it('should route resolved listener detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + function listenerAPI() { + var listener = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(listener); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + spyOn(lbaasv2API, 'getListener').and.callFake(listenerAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/listeners/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1/listeners/2'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + }); + })); + + it('should route resolved pool detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + function listenerAPI() { + var listener = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(listener); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function poolAPI() { + var pool = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(pool); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + spyOn(lbaasv2API, 'getListener').and.callFake(listenerAPI); + spyOn(lbaasv2API, 'getPool').and.callFake(poolAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/pools/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1/listeners/2/pools/3'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + }); + })); + + it('should route resolved member detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + function listenerAPI() { + var listener = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(listener); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function poolAPI() { + var pool = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(pool); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function memberAPI() { + var member = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(member); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + spyOn(lbaasv2API, 'getListener').and.callFake(listenerAPI); + spyOn(lbaasv2API, 'getPool').and.callFake(poolAPI); + spyOn(lbaasv2API, 'getMember').and.callFake(memberAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/members/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1/listeners/2/pools/3/members/4'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + }); + })); + + it('should route resolved health monitor detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + function listenerAPI() { + var listener = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(listener); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function poolAPI() { + var pool = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(pool); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function healthmonitorAPI() { + var healthmonitor = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(healthmonitor); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + spyOn(lbaasv2API, 'getListener').and.callFake(listenerAPI); + spyOn(lbaasv2API, 'getPool').and.callFake(poolAPI); + spyOn(lbaasv2API, 'getHealthMonitor').and.callFake(healthmonitorAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/healthmonitors/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1/listeners/2/pools/3/healthmonitors/4'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + }); + })); + + it('should redirect to project home on route change error', + inject(function($location, $rootScope) { + spyOn($location, 'path').and.callThrough(); + $rootScope.$emit('$routeChangeError', null, null, null, 'routeChangeError'); + expect($location.path).toHaveBeenCalledWith('project/load_balancer'); + }) + ); + }); })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.scss b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.scss index e20e7d64..408e3ab2 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.scss +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.scss @@ -1,5 +1,6 @@ /* * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -74,9 +75,8 @@ } } -/* Progress indicator while data is loading */ -[table-status], -detail-status { +/* Progress indicator in the table while items are loading */ +[table-status] { .progress { margin: 0px auto; width: 25%; @@ -86,12 +86,9 @@ detail-status { } } } -detail-status { - .progress { - margin-top: 25vh; - } - .error-actions { - text-align: center; + +.octavia-tabset { + .tab-content { margin-top: 10px; } } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.js deleted file mode 100644 index 5d3b1537..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.lbaasv2.listeners') - .factory('horizon.dashboard.project.lbaasv2.listeners.actions.batchActions', - tableBatchActions); - - tableBatchActions.$inject = [ - '$q', - '$location', - 'horizon.dashboard.project.lbaasv2.workflow.modal', - 'horizon.app.core.openstack-service-api.policy', - 'horizon.framework.util.i18n.gettext', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.listeners.actions.delete' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.listeners.actions.batchActions - * - * @description - * Provides the service for the Listeners table batch actions. - * - * @param $q The angular service for promises. - * @param $location The angular $location service. - * @param workflowModal The LBaaS workflow modal service. - * @param policy The horizon policy service. - * @param gettext The horizon gettext function for translation. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param deleteService The LBaaS v2 listeners delete service. - * @returns Listeners table batch actions service object. - */ - - function tableBatchActions( - $q, $location, workflowModal, policy, gettext, loadBalancersService, deleteService - ) { - var loadBalancerIsActionable, loadBalancerId; - - var create = workflowModal.init({ - controller: 'CreateListenerWizardController', - message: gettext('A new listener is being created.'), - handle: onCreate, - allowed: canCreate - }); - - var service = { - actions: actions, - init: init - }; - - return service; - - /////////////// - - function init(_loadBalancerId_) { - loadBalancerId = _loadBalancerId_; - loadBalancerIsActionable = loadBalancersService.isActionable(loadBalancerId); - return service; - } - - function actions() { - return [{ - service: create, - template: { - type: 'create', - text: gettext('Create Listener') - } - },{ - service: deleteService.init(loadBalancerId, loadBalancerIsActionable), - template: { - text: gettext('Delete Listeners'), - type: 'delete-selected' - } - }]; - } - - function canCreate() { - return $q.all([ - loadBalancerIsActionable, - policy.ifAllowed({ rules: [['neutron', 'create_listener']] }) - ]); - } - - function onCreate(response) { - var id = response.data.id; - $location.path('project/load_balancer/' + loadBalancerId + '/listeners/' + id); - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.spec.js deleted file mode 100644 index 35ec5a62..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/batch-actions.service.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Listeners Table Batch Actions Service', function() { - var $location, actions, policy, $scope, $q; - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - var response = { - data: { - id: '5678' - } - }; - var modal = { - open: function() { - return { - result: { - then: function(func) { - func(response); - } - } - }; - } - }; - $provide.value('$uibModal', modal); - $provide.value('horizon.dashboard.project.lbaasv2.loadbalancers.service', { - isActionable: function() { - return $q.when(); - } - }); - $provide.value('horizon.app.core.openstack-service-api.policy', { - ifAllowed: function() { - return $q.when(); - } - }); - })); - - beforeEach(inject(function ($injector) { - $location = $injector.get('$location'); - $scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - var batchActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.listeners.actions.batchActions'); - actions = batchActionsService.init('1234').actions(); - $scope.$apply(); - })); - - it('should define correct table batch actions', function() { - expect(actions.length).toBe(2); - expect(actions[0].template.text).toBe('Create Listener'); - expect(actions[1].template.text).toBe('Delete Listeners'); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - it('should check policy to allow creating a listener', function() { - spyOn(policy, 'ifAllowed').and.callThrough(); - - var promise = actions[0].service.allowed(); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - $scope.$apply(); - - expect(allowed).toBe(true); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'create_listener']]}); - }); - - it('should redirect after create', function() { - spyOn($location, 'path').and.callThrough(); - actions[0].service.perform(); - expect($location.path).toHaveBeenCalledWith('project/load_balancer/1234/listeners/5678'); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.js new file mode 100644 index 00000000..22245452 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.js @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Walmart. + * + * 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.lbaasv2.listeners') + .factory('horizon.dashboard.project.lbaasv2.listeners.actions.create', + createService); + + createService.$inject = [ + 'horizon.dashboard.project.lbaasv2.listeners.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc service + * @ngname horizon.dashboard.project.lbaasv2.listeners.actions.batchActions + * + * @description + * Provides the service for the Listeners creation. + * + * @param resourceType The listener resource type. + * @param actionResultService The horizon action result service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param gettext The horizon gettext function for translation. + * + * @returns Listeners create service object. + */ + + function createService( + resourceType, actionResultService, workflowModal, policy, gettext + ) { + return workflowModal.init({ + controller: 'CreateListenerWizardController', + message: gettext('A new listener is being created.'), + handle: handle, + allowed: allowed + }); + + function allowed() { + return policy.ifAllowed({ rules: [['neutron', 'create_listener']] }); + } + + function handle(response) { + return actionResultService.getActionResult() + .created(resourceType, response.data.id) + .result; + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.spec.js new file mode 100644 index 00000000..b24c078d --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/create/create.service.spec.js @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Create Listener Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'listener1' } }); + } + } + }; + } + }); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.listeners.actions.create'); + })); + + it('should check policy to allow creating a listener', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'create_listener']]}); + }); + + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); + }); + + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js index fba8caf8..8d502720 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,9 +22,10 @@ .factory('horizon.dashboard.project.lbaasv2.listeners.actions.delete', deleteService); deleteService.$inject = [ + 'horizon.dashboard.project.lbaasv2.listeners.resourceType', + 'horizon.framework.util.actions.action-result.service', '$q', '$location', - '$route', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.policy', @@ -35,26 +37,31 @@ /** * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.listeners.actions.deleteService + * * @description * Brings up the delete listeners confirmation modal dialog. * On submit, deletes selected listeners. * On cancel, does nothing. + * + * @param resourceType The listener resource type. + * @param actionResultService The horizon action result service. * @param $q The angular service for promises. * @param $location The angular $location service. - * @param $route The angular $route service. * @param deleteModal The horizon delete modal service. * @param api The LBaaS v2 API service. * @param policy The horizon policy service. * @param toast The horizon message service. * @param qExtensions Horizon extensions to the $q service. * @param gettext The horizon gettext function for translation. - * @returns The listeners table delete service. + * + * @returns The listeners delete service. */ function deleteService( - $q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext + resourceType, actionResultService, $q, $location, + deleteModal, api, policy, toast, qExtensions, gettext ) { - var loadbalancerId, statePromise; + var loadbalancerId, scope; var notAllowedMessage = gettext('The following listeners will not be deleted ' + 'due to existing pools: %s.'); var context = { @@ -74,54 +81,38 @@ var service = { perform: perform, - allowed: allowed, - init: init + allowed: allowed }; return service; ////////////// - function init(_loadbalancerId_, _statePromise_) { - loadbalancerId = _loadbalancerId_; - statePromise = _statePromise_; - return service; + function perform(items, _scope_) { + scope = _scope_; + var listeners = angular.isArray(items) ? items : [items]; + listeners.map(function(item) { + loadbalancerId = item.loadbalancerId; + }); + return qExtensions.allSettled(listeners.map(checkPermission)).then(afterCheck); } - function perform(items) { - if (angular.isArray(items)) { - qExtensions.allSettled(items.map(checkPermission)).then(afterCheck); - } else { - deleteModal.open({ $emit: actionComplete }, [items], context); - } - } - - function allowed(item) { - var promises = [policy.ifAllowed({ rules: [['neutron', 'delete_listener']] }), statePromise]; - if (item) { - promises.push(qExtensions.booleanAsPromise(!item.default_pool_id)); - } - return $q.all(promises); - } - - function deleteItem(id) { - return api.deleteListener(id, true); - } - - function actionComplete(eventType) { - if (eventType === context.failedEvent) { - // Action failed, reload the page - $route.reload(); - } else { - // If the user is on the listeners table then just reload the page, otherwise they - // are on the details page and we return to the table. - var regex = new RegExp('project\/load_balancer\/' + loadbalancerId + '(\/)?$'); - if (regex.test($location.path())) { - $route.reload(); - } else { - $location.path('project/load_balancer/' + loadbalancerId); - } + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { + var path = 'project/load_balancer/' + loadbalancerId; + $location.path(path); } + return actionResult.result; } function checkPermission(item) { @@ -133,7 +124,7 @@ toast.add('error', getMessage(notAllowedMessage, result.fail)); } if (result.pass.length > 0) { - deleteModal.open({ $emit: actionComplete }, result.pass.map(getEntity), context); + return deleteModal.open(scope, result.pass.map(getEntity), context).then(deleteResult); } } @@ -153,5 +144,12 @@ return result.context; } + function allowed() { + return policy.ifAllowed({ rules: [['neutron', 'delete_listener']] }); + } + + function deleteItem(id) { + return api.deleteListener(id, true); + } } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js index 43904dce..3fc763ab 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/delete/delete.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,17 +18,11 @@ 'use strict'; describe('LBaaS v2 Listeners Delete Service', function() { - var service, policy, modal, lbaasv2Api, $scope, $route, $location, $q, toast, items, path; + var service, policy, modal, lbaasv2Api, $scope, $location, $q, toast, items, path; function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(makePromise()); - var promise = service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); + spyOn(policy, 'ifAllowed').and.returnValue(true); + var allowed = service.allowed(item); $scope.$apply(); expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'delete_listener']]}); return allowed; @@ -46,15 +41,19 @@ beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(function() { - items = [{ id: '1', name: 'First' }, - { id: '2', name: 'Second' }]; + items = [{ id: '1', name: 'First', loadbalancerId: 1 }, + { id: '2', name: 'Second', loadbalancerId: 1 }]; }); beforeEach(module(function($provide) { $provide.value('$uibModal', { open: function() { return { - result: makePromise() + result: { + then: function(func) { + return func({ data: { id: 'listener1' } }); + } + } }; } }); @@ -75,12 +74,10 @@ lbaasv2Api = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); modal = $injector.get('horizon.framework.widgets.modal.deleteModalService'); $scope = $injector.get('$rootScope').$new(); - $route = $injector.get('$route'); $location = $injector.get('$location'); $q = $injector.get('$q'); toast = $injector.get('horizon.framework.widgets.toast.service'); service = $injector.get('horizon.dashboard.project.lbaasv2.listeners.actions.delete'); - service.init('1', makePromise()); })); it('should have the "allowed" and "perform" functions', function() { @@ -96,23 +93,14 @@ expect(allowed()).toBe(true); }); - it('should not allow deleting a listener from load balancer in a PENDING state', function() { - service.init('1', makePromise(true)); - expect(allowed()).toBe(false); - }); - - it('should not allow deleting a listener that has a default pool', function() { - expect(allowed({default_pool_id: 'pool1'})).toBe(false); - }); - it('should open the delete modal', function() { - spyOn(modal, 'open'); - service.perform(items[0]); + spyOn(modal, 'open').and.callThrough(); + service.perform(items[0], $scope); $scope.$apply(); expect(modal.open.calls.count()).toBe(1); var args = modal.open.calls.argsFor(0); expect(args.length).toBe(3); - expect(args[0]).toEqual({ $emit: jasmine.any(Function) }); + expect(args[0]).toEqual($scope); expect(args[1]).toEqual([jasmine.objectContaining({ id: '1' })]); expect(args[2]).toEqual(jasmine.objectContaining({ labels: jasmine.any(Object), @@ -124,7 +112,7 @@ it('should pass function to modal that deletes listeners', function() { spyOn(modal, 'open').and.callThrough(); spyOn(lbaasv2Api, 'deleteListener').and.callThrough(); - service.perform(items[0]); + service.perform(items[0], $scope); $scope.$apply(); expect(lbaasv2Api.deleteListener.calls.count()).toBe(1); expect(lbaasv2Api.deleteListener).toHaveBeenCalledWith('1', true); @@ -135,7 +123,7 @@ spyOn(lbaasv2Api, 'deleteListener').and.returnValue(makePromise(true)); spyOn(toast, 'add'); items.splice(1, 1); - service.perform(items); + service.perform(items, $scope); $scope.$apply(); expect(modal.open).toHaveBeenCalled(); expect(lbaasv2Api.deleteListener.calls.count()).toBe(1); @@ -143,19 +131,11 @@ 'be deleted, possibly due to existing pools: First.'); }); - it('should reload table after delete', function() { - path = 'project/load_balancer/1'; - spyOn($route, 'reload'); - service.perform(items); - $scope.$apply(); - expect($route.reload).toHaveBeenCalled(); - }); - it('should return to table after delete if on detail page', function() { path = 'project/load_balancer/1/listeners/2'; spyOn($location, 'path'); spyOn(toast, 'add'); - service.perform(items[0]); + service.perform(items[0], $scope); $scope.$apply(); expect($location.path).toHaveBeenCalledWith('project/load_balancer/1'); expect(toast.add).toHaveBeenCalledWith('success', 'Deleted listeners: First.'); @@ -166,7 +146,7 @@ spyOn(toast, 'add'); items[0].default_pool_id = 'pool1'; items[1].default_pool_id = 'pool2'; - service.perform(items); + service.perform(items, $scope); $scope.$apply(); expect(modal.open).not.toHaveBeenCalled(); expect(toast.add).toHaveBeenCalledWith('error', diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.js new file mode 100644 index 00000000..c59a6da4 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.js @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Walmart. + * + * 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.lbaasv2.listeners') + .factory('horizon.dashboard.project.lbaasv2.listeners.actions.edit', + editService); + + editService.$inject = [ + 'horizon.dashboard.project.lbaasv2.listeners.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc service + * @ngname horizon.dashboard.project.lbaasv2.listeners.actions.edit + * + * @description + * Provides the service for the Listener edit action. + * + * @param resourceType The listener resource type. + * @param actionResultService The horizon action result service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param gettext The horizon gettext function for translation. + * + * @returns Listeners edit action service object. + */ + + function editService( + resourceType, actionResultService, workflowModal, policy, gettext + ) { + return workflowModal.init({ + controller: 'EditListenerWizardController', + message: gettext('The listener has been updated.'), + handle: handle, + allowed: allowed + }); + + function allowed(/*item*/) { + return policy.ifAllowed({ rules: [['neutron', 'update_listener']] }); + } + + function handle(response) { + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.listener.id) + .result; + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.spec.js new file mode 100644 index 00000000..6ee839af --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/edit/edit.service.spec.js @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Edit Listener Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'listener1' } }); + } + } + }; + } + }); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.listeners.actions.edit'); + })); + + it('should check policy to allow editing a listener', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_listener']]}); + }); + + it('should handle the action result properly', function() { + var result = service.handle({config: {data: {listener: {id: 1}}}}); + expect(result.updated[0].id).toBe(1); + }); + + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js deleted file mode 100644 index bb62eac3..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.lbaasv2.listeners') - .factory('horizon.dashboard.project.lbaasv2.listeners.actions.rowActions', - tableRowActions); - - tableRowActions.$inject = [ - '$q', - '$route', - 'horizon.dashboard.project.lbaasv2.workflow.modal', - 'horizon.app.core.openstack-service-api.policy', - 'horizon.framework.util.i18n.gettext', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.listeners.actions.delete', - 'horizon.dashboard.project.lbaasv2.pools.actions.create' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.listeners.actions.rowActions - * - * @description - * Provides the service for the Listener table row actions. - * - * @param $q The angular service for promises. - * @param $route The angular $route service. - * @param workflowModal The LBaaS workflow modal service. - * @param policy The horizon policy service. - * @param gettext The horizon gettext function for translation. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param deleteService The LBaaS v2 listeners delete service. - * @param createPoolService The LBaaS v2 pools create service. - * @returns Listeners row actions service object. - */ - - function tableRowActions( - $q, $route, workflowModal, policy, gettext, loadBalancersService, deleteService, - createPoolService - ) { - var loadbalancerId, loadBalancerIsActionable; - - var edit = workflowModal.init({ - controller: 'EditListenerWizardController', - message: gettext('The listener has been updated.'), - handle: onEdit, - allowed: canEdit - }); - - var service = { - actions: actions, - init: init - }; - - return service; - - /////////////// - - function init(_loadbalancerId_) { - loadbalancerId = _loadbalancerId_; - loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId); - return service; - } - - function actions() { - return [{ - service: edit, - template: { - text: gettext('Edit') - } - },{ - service: createPoolService.init(loadbalancerId, loadBalancerIsActionable).create, - template: { - text: gettext('Create Pool') - } - },{ - service: deleteService.init(loadbalancerId, loadBalancerIsActionable), - template: { - text: gettext('Delete Listener'), - type: 'delete' - } - }]; - } - - function canEdit(/*item*/) { - return $q.all([ - loadBalancerIsActionable, - policy.ifAllowed({ rules: [['neutron', 'update_listener']] }) - ]); - } - - function onEdit(/*response*/) { - $route.reload(); - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js deleted file mode 100644 index 15debc82..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Listeners Table Row Actions Service', function() { - var scope, $route, $q, actions, policy, init; - - function canEdit(item) { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = actions[0].service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_listener']]}); - return allowed; - } - - function isActionableMock(id) { - if (id === 'active') { - return $q.when(); - } else { - return $q.reject(); - } - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - var response = { - data: { - id: '1' - } - }; - var modal = { - open: function() { - return { - result: { - then: function(func) { - func(response); - } - } - }; - } - }; - $provide.value('$uibModal', modal); - })); - - beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); - $route = $injector.get('$route'); - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - var rowActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.listeners.actions.rowActions'); - actions = rowActionsService.actions(); - init = rowActionsService.init; - var loadbalancerService = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.service'); - spyOn(loadbalancerService, 'isActionable').and.callFake(isActionableMock); - })); - - it('should define correct table row actions', function() { - expect(actions.length).toBe(3); - expect(actions[0].template.text).toBe('Edit'); - expect(actions[1].template.text).toBe('Create Pool'); - expect(actions[2].template.text).toBe('Delete Listener'); - }); - - it('should allow editing a listener of an ACTIVE load balancer', function() { - init('active'); - expect(canEdit({listenerId: '1234'})).toBe(true); - }); - - it('should not allow editing a listener of a non-ACTIVE load balancer', function() { - init('non-active'); - expect(canEdit({listenerId: '1234'})).toBe(false); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - it('should reload table after edit', function() { - spyOn($route, 'reload').and.callThrough(); - actions[0].service.perform(); - expect($route.reload).toHaveBeenCalled(); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.js deleted file mode 100644 index 3fe9dc33..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.lbaasv2.listeners') - .controller('ListenerDetailController', ListenerDetailController); - - ListenerDetailController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.listeners.actions.rowActions', - '$routeParams', - '$q' - ]; - - /** - * @ngdoc controller - * @name ListenerDetailController - * - * @description - * Controller for the LBaaS v2 listener detail page. - * - * @param api The LBaaS v2 API service. - * @param rowActions The listener row actions service. - * @param $routeParams The angular $routeParams service. - * @param $q The angular service for promises. - * @returns undefined - */ - - function ListenerDetailController(api, rowActions, $routeParams, $q) { - var ctrl = this; - - ctrl.loading = true; - ctrl.error = false; - ctrl.actions = rowActions.init($routeParams.loadbalancerId).actions; - - init(); - - //////////////////////////////// - - function init() { - ctrl.listener = null; - ctrl.loadbalancer = null; - ctrl.loading = true; - ctrl.error = false; - $q.all([ - api.getListener($routeParams.listenerId) - .then(success('listener'), fail('listener')), - api.getLoadBalancer($routeParams.loadbalancerId) - .then(success('loadbalancer'), fail('loadbalancer')) - ]).then(postInit, initError); - } - - function success(property) { - return angular.bind(null, function setProp(property, response) { - ctrl[property] = response.data; - }, property); - } - - function fail(property) { - return angular.bind(null, function setProp(property, error) { - ctrl[property] = null; - throw error; - }, property); - } - - function postInit() { - ctrl.loading = false; - } - - function initError() { - ctrl.loading = false; - ctrl.error = true; - } - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.spec.js deleted file mode 100644 index 586fd2c4..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.controller.spec.js +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Listener Detail Controller', function() { - var lbaasv2API, $controller, apiFail, qAllFail; - - function fakePromise(data, reject) { - return { - then: function(success, fail) { - if (reject) { - fail(); - } else { - success({ data: data }); - } - return fakePromise(); - } - }; - } - - function fakeAPI() { - return fakePromise('foo', apiFail); - } - - function loadbalancerAPI() { - return fakePromise({ provisioning_status: 'ACTIVE' }); - } - - function qAll() { - return fakePromise(null, qAllFail); - } - - function createController() { - return $controller('ListenerDetailController', { - $routeParams: { - loadbalancerId: 'loadbalancerId', - listenerId: 'listenerId' - } - }); - } - - /////////////////////// - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - apiFail = false; - qAllFail = false; - - $provide.value('$q', { all: qAll }); - $provide.value('$uibModal', {}); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); - $controller = $injector.get('$controller'); - })); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getListener).toHaveBeenCalledWith('listenerId'); - expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('loadbalancerId'); - expect(ctrl.loadbalancer).toEqual({ provisioning_status: 'ACTIVE' }); - expect(ctrl.listener).toBe('foo'); - }); - - it('should throw error on API fail', function() { - apiFail = true; - var init = function() { - createController(); - }; - expect(init).toThrow(); - }); - - it('should set error state if any APIs fail', function() { - qAllFail = true; - var ctrl = createController(); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(true); - }); - - }); - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.html deleted file mode 100644 index 3ab4ddbd..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/detail.html +++ /dev/null @@ -1,46 +0,0 @@ -
- -
- -
-
-
-
Protocol
-
{$ ::ctrl.listener.protocol $}
-
Protocol Port
-
{$ ::ctrl.listener.protocol_port $}
-
Connection Limit
-
{$ ctrl.listener.connection_limit | limit $}
-
Admin State Up
-
{$ ctrl.listener.admin_state_up | yesno $}
-
Default Pool ID
-
- - {$ ::ctrl.listener.default_pool_id $} - - - {$ 'None' | translate $} - -
-
Listener ID
-
{$ ::ctrl.listener.id $}
-
Project ID
-
{$ ::ctrl.listener.project_id $}
-
Created At
-
{$ ::ctrl.listener.created_at $}
-
Updated At
-
{$ ::ctrl.listener.updated_at $}
-
-
-
-
-
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js new file mode 100644 index 00000000..4e450614 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js @@ -0,0 +1,101 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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.lbaasv2.listeners') + .controller('ListenerDetailController', ListenerDetailController); + + ListenerDetailController.$inject = [ + 'loadbalancer', + 'listener', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.listeners.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name ListenerDetailController + * + * @description + * Controller for the LBaaS v2 listener detail page. + * + * @param loadbalancer The loadbalancer object. + * @param listener The listener object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The listenr resource type. + * @param typeRegistry The horizon resource type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function ListenerDetailController( + loadbalancer, listener, loadBalancersService, resourceType, typeRegistry, + spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.loadbalancer = loadbalancer; + ctrl.listener = listener; + ctrl.listFunctionExtraParams = { + loadbalancerId: ctrl.loadbalancer.id, + listenerId: ctrl.listener.id + }; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.identifier = listener.id; + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.listener = response.data; + ctrl.listener.loadbalancerId = ctrl.loadbalancer.id; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); + ctrl.context.loadPromise.then(loadData); + } + } + + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js new file mode 100644 index 00000000..b11810bc --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js @@ -0,0 +1,101 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Listener Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('ListenerDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + listener: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + expect(ctrl.listener).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing'}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + }); + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html new file mode 100644 index 00000000..82c3acba --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html @@ -0,0 +1,62 @@ + + + +
+ + +
+
+ + + + +
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/drawer.html b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/drawer.html new file mode 100644 index 00000000..cec0fec1 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/drawer.html @@ -0,0 +1,9 @@ + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js index f97cae75..07515593 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -26,6 +27,152 @@ */ angular - .module('horizon.dashboard.project.lbaasv2.listeners', []); + .module('horizon.dashboard.project.lbaasv2.listeners', []) + .constant('horizon.dashboard.project.lbaasv2.listeners.resourceType', + 'OS::Octavia::Listener') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.listeners.actions.create', + 'horizon.dashboard.project.lbaasv2.listeners.actions.edit', + 'horizon.dashboard.project.lbaasv2.listeners.actions.delete', + 'horizon.dashboard.project.lbaasv2.listeners.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + deleteService, + resourceType + ) { + var listenerResourceType = registry.getResourceType(resourceType); + + listenerResourceType + .setNames(gettext('Listener'), gettext('Listeners')) + .setSummaryTemplateUrl(basePath + 'listeners/details/drawer.html') + .setProperties(listenerProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getListenersPromise) + .setLoadFunction(loadBalancerService.getListenerPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + urlFunction: loadBalancerService.getListenerDetailsPath + }) + .append({ + id: 'protocol', + priority: 1 + }) + .append({ + id: 'protocol_port', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + listenerResourceType.itemActions + .append({ + id: 'listenerEdit', + service: editService, + template: { + text: gettext('Edit Listener') + } + }) + .append({ + id: 'listenerDelete', + service: deleteService, + template: { + text: gettext('Delete Listener'), + type: 'delete' + } + }); + + listenerResourceType.globalActions + .append({ + id: 'listenerCreate', + service: createService, + template: { + type: 'create', + text: gettext('Create Listener') + } + }); + + listenerResourceType.batchActions + .append({ + id: 'listenerBatchDelete', + service: deleteService, + template: { + text: gettext('Delete Listeners'), + type: 'delete-selected' + } + }); + } + + function listenerProperties(loadBalancerService) { + return { + id: gettext('ID'), + name: { + label: gettext('Name'), + filters: ['noName'] + }, + description: { + label: gettext('Description'), + filters: ['noValue'] + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + protocol: gettext('Protocol'), + protocol_port: gettext('Port'), + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + }, + connection_limit: { + label: gettext('Connection Limit'), + filters: ['limit'] + }, + default_tls_container_ref: gettext('Default TLS Container Ref'), + sni_container_refs: gettext('SNI Container Refs'), + default_pool_id: { + label: gettext('Default Pool ID'), + filters: ['noName'] + }, + l7_policies: gettext('L7 Policies'), + insert_headers: gettext('Insert Headers'), + load_balancers: gettext('Load Balancers') + }; + } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.spec.js index 9cc8f684..48bb71d9 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -22,4 +23,46 @@ }); }); + describe('LBaaS v2 Listeners Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.listeners.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'listenerEdit')).toBe(true); + expect(actionHasId(actions, 'listenerDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'listenerCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'listenerBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.js deleted file mode 100644 index cfe3b1f4..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.lbaasv2.listeners') - .controller('ListenersTableController', ListenersTableController); - - ListenersTableController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - '$routeParams', - 'horizon.dashboard.project.lbaasv2.listeners.actions.rowActions', - 'horizon.dashboard.project.lbaasv2.listeners.actions.batchActions' - ]; - - /** - * @ngdoc controller - * @name ListenersTableController - * - * @description - * Controller for the LBaaS v2 listeners table. Serves as the focal point for table actions. - * - * @param api The LBaaS V2 service API. - * @param $routeParams The angular $routeParams service. - * @param rowActions The listener row actions service. - * @param batchActions The listener batch actions service. - * @returns undefined - */ - - function ListenersTableController(api, $routeParams, rowActions, batchActions) { - - var ctrl = this; - ctrl.items = []; - ctrl.src = []; - ctrl.loading = true; - ctrl.error = false; - ctrl.checked = {}; - ctrl.loadbalancerId = $routeParams.loadbalancerId; - ctrl.batchActions = batchActions.init(ctrl.loadbalancerId); - ctrl.rowActions = rowActions.init(ctrl.loadbalancerId); - - init(); - - //////////////////////////////// - - function init() { - ctrl.src = []; - ctrl.loading = true; - ctrl.error = false; - api.getListeners(ctrl.loadbalancerId).then(success, fail); - } - - function success(response) { - ctrl.src = response.data.items; - ctrl.loading = false; - } - - function fail(/*response*/) { - ctrl.src = []; - ctrl.loading = false; - ctrl.error = true; - } - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.spec.js deleted file mode 100644 index 379f12d2..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.controller.spec.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Listeners Table Controller', function() { - var controller, lbaasv2API, rowActions, batchActions; - var items = [{ foo: 'bar' }]; - var apiFail = false; - - function fakeAPI() { - return { - then: function(success, fail) { - if (apiFail && fail) { - fail(); - } else { - success({ data: { items: items } }); - } - } - }; - } - - function initMock() { - return rowActions; - } - - /////////////////////// - - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - $provide.value('$uibModal', {}); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - controller = $injector.get('$controller'); - rowActions = $injector.get('horizon.dashboard.project.lbaasv2.listeners.actions.rowActions'); - batchActions = $injector.get( - 'horizon.dashboard.project.lbaasv2.listeners.actions.batchActions'); - spyOn(rowActions, 'init').and.callFake(initMock); - spyOn(lbaasv2API, 'getListeners').and.callFake(fakeAPI); - })); - - function createController() { - return controller('ListenersTableController', { - $routeParams: { loadbalancerId: '1234' } - }); - } - - it('should initialize correctly', function() { - var ctrl = createController(); - expect(ctrl.items).toEqual([]); - expect(ctrl.src).toEqual(items); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(false); - expect(ctrl.checked).toEqual({}); - expect(ctrl.loadbalancerId).toEqual('1234'); - expect(rowActions.init).toHaveBeenCalledWith(ctrl.loadbalancerId); - expect(ctrl.rowActions).toBeDefined(); - expect(ctrl.rowActions).toEqual(rowActions); - expect(ctrl.batchActions).toBeDefined(); - expect(ctrl.batchActions).toEqual(batchActions); - }); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getListeners).toHaveBeenCalled(); - expect(ctrl.src.length).toBe(1); - }); - - it('should show error if loading fails', function() { - apiFail = true; - var ctrl = createController(); - expect(ctrl.src.length).toBe(0); - expect(ctrl.error).toBe(true); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.html b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.html deleted file mode 100644 index 4376051b..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/table.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - NameDescriptionProtocolPortActions
- - - - - {$ ::(item.name || item.id) $}{$ ::item.description | noValue $}{$ ::item.protocol$}{$ ::item.protocol_port$} - - -
- -
-
-
ID
-
{$ ::item.id $}
-
-
-
Admin State Up
-
{$ ::item.admin_state_up | yesno $}
-
-
-
Connection Limit
-
{$ ::item.connection_limit | limit $}
-
-
-
Default Pool ID
-
{$ ::item.default_pool_id | noValue $}
-
-
- -
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.controller.spec.js index f0eb5444..622d01a4 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.controller.spec.js @@ -20,7 +20,6 @@ var ctrl, network, floatingIps, floatingIpPools, $controller, $uibModalInstance; var associateFail = false; - beforeEach(module('horizon.framework.util.i18n')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(function() { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.js index cec7389f..a3303cfd 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -18,7 +19,7 @@ angular .module('horizon.dashboard.project.lbaasv2.loadbalancers') - .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service', + .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip', modalService); modalService.$inject = [ @@ -35,7 +36,7 @@ /** * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service + * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip * * @description * Provides the service for the Load Balancer Associate Floating IP action. diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.spec.js index a70c0334..bd78ba98 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/associate-ip/modal.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -34,10 +35,6 @@ return allowed; } - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(function() { @@ -76,7 +73,7 @@ $route = $injector.get('$route'); $uibModal = $injector.get('$uibModal'); service = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service'); + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip'); })); it('should have the "allowed" and "perform" functions', function() { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.spec.js deleted file mode 100644 index 8095b4b2..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2015 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'; - - describe('LBaaS v2 Load Balancers Table Batch Actions Service', function() { - var $location, actions, policy; - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - var response = { - data: { - id: '1' - } - }; - var modal = { - open: function() { - return { - result: { - then: function(func) { - func(response); - } - } - }; - } - }; - $provide.value('$uibModal', modal); - })); - - beforeEach(inject(function ($injector) { - $location = $injector.get('$location'); - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - var batchActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions'); - actions = batchActionsService.actions(); - })); - - it('should define correct table batch actions', function() { - expect(actions.length).toBe(2); - expect(actions[0].template.text).toBe('Create Load Balancer'); - expect(actions[1].template.text).toBe('Delete Load Balancers'); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - it('should check policy to allow creating a load balancer', function() { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var allowed = actions[0].service.allowed(); - expect(allowed).toBe(true); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'create_loadbalancer']]}); - }); - - it('should redirect after create', function() { - spyOn($location, 'path').and.callThrough(); - actions[0].service.perform(); - expect($location.path).toHaveBeenCalledWith('project/load_balancer/1'); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.js similarity index 54% rename from octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.js rename to octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.js index 5684733f..9f52d7ac 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.js @@ -1,5 +1,6 @@ /* - * Copyright 2015 IBM Corp. + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -18,76 +19,55 @@ angular .module('horizon.dashboard.project.lbaasv2.loadbalancers') - .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions', - tableBatchActions); + .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create', createService); - tableBatchActions.$inject = [ - '$location', + createService.$inject = [ + 'horizon.dashboard.project.lbaasv2.loadbalancers.resourceType', + 'horizon.framework.util.actions.action-result.service', 'horizon.dashboard.project.lbaasv2.workflow.modal', - 'horizon.dashboard.project.lbaasv2.basePath', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.delete', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.i18n.gettext' ]; /** * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions + * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.create * * @description - * Provides the service for the Load Balancers table batch actions. + * Provides the service for the create load balancer action. * - * @param $location The angular $location service. + * @param resourceType The loadbalancer resource type. + * @param actionResultService The horizon action result service. * @param workflowModal The LBaaS workflow modal service. - * @param basePath The lbaasv2 module base path. - * @param deleteService The load balancer delete service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. - * @returns Load balancers table batch actions service object. + * + * @returns Create load balancer action service. */ - function tableBatchActions($location, workflowModal, basePath, deleteService, policy, gettext) { + function createService( + resourceType, actionResultService, workflowModal, policy, gettext + ) { - var create = workflowModal.init({ + return workflowModal.init({ controller: 'CreateLoadBalancerWizardController', message: gettext('A new load balancer is being created.'), - handle: onCreate, - allowed: canCreate + handle: handle, + allowed: allowed }); - var service = { - actions: actions - }; - - return service; - - /////////////// - - function actions() { - return [{ - service: create, - template: { - type: 'create', - text: gettext('Create Load Balancer') - } - }, { - service: deleteService, - template: { - type: 'delete-selected', - text: gettext('Delete Load Balancers') - } - }]; - } - - function canCreate() { + function allowed() { // This rule is made up and should therefore always pass. I assume at some point there // will be a valid rule similar to this that we will want to use. return policy.ifAllowed({ rules: [['neutron', 'create_loadbalancer']] }); } - function onCreate(response) { - $location.path('project/load_balancer/' + response.data.id); + function handle(response) { + return actionResultService.getActionResult() + .created(resourceType, response.data.id) + .result; } + } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.spec.js new file mode 100644 index 00000000..d4c9ca08 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/create.service.spec.js @@ -0,0 +1,56 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Create Load Balancer Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'loadbalancer1' } }); + } + } + }; + } + }); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create'); + })); + + it('should check policy to allow creating a load balancer', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'create_loadbalancer']]}); + }); + + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); + }); + + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.spec.js index c5b94230..e7ff01c1 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.spec.js @@ -29,7 +29,6 @@ }; var scope = {}; - beforeEach(module('horizon.framework.util')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(module(function ($provide) { $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.js index 26b765ac..9620690c 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,9 +22,9 @@ .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.delete', deleteService); deleteService.$inject = [ - '$q', + 'horizon.dashboard.project.lbaasv2.loadbalancers.resourceType', + 'horizon.framework.util.actions.action-result.service', '$location', - '$route', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.policy', @@ -36,24 +37,31 @@ * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.loadbalancers.actions.deleteService * @description + * * Brings up the delete load balancers confirmation modal dialog. * On submit, deletes selected load balancers. * On cancel, does nothing. - * @param $q The angular service for promises. + * + * @param resourceType The loadbalancer resource type. + * @param actionResultService The horizon action result service. * @param $location The angular $location service. - * @param $route The angular $route service. * @param deleteModal The horizon delete modal service. * @param api The LBaaS v2 API service. * @param policy The horizon policy service. * @param toast The horizon message service. * @param qExtensions Horizon extensions to the $q service. * @param gettext The horizon gettext function for translation. - * @returns The load balancers table delete service. + * + * @returns The load balancers delete service. */ function deleteService( - $q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext + resourceType, actionResultService, $location, deleteModal, api, + policy, toast, qExtensions, gettext ) { + + var scope; + // If a batch delete, then this message is displayed for any selected load balancers not in // ACTIVE or ERROR state. var notAllowedMessage = gettext('The following load balancers are pending and cannot be ' + @@ -82,23 +90,34 @@ ////////////// - function perform(items) { - if (angular.isArray(items)) { - qExtensions.allSettled(items.map(checkPermission)).then(afterCheck); - } else { - deleteModal.open({ $emit: actionComplete }, [items], context); - } + function perform(items, _scope_) { + scope = _scope_; + var loadbalancers = angular.isArray(items) ? items : [items]; + return qExtensions.allSettled(loadbalancers.map(checkPermission)).then(afterCheck); } - function allowed(item) { + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { + var path = 'project/load_balancer'; + $location.path(path); + } + return actionResult.result; + } + + function allowed() { // This rule is made up and should therefore always pass. I assume at some point there // will be a valid rule similar to this that we will want to use. - var promises = [policy.ifAllowed({ rules: [['neutron', 'delete_loadbalancer']] })]; - if (item) { - var status = item.provisioning_status; - promises.push(qExtensions.booleanAsPromise(status === 'ACTIVE' || status === 'ERROR')); - } - return $q.all(promises); + return policy.ifAllowed({ rules: [['neutron', 'delete_loadbalancer']] }); } function canBeDeleted(item) { @@ -115,7 +134,7 @@ toast.add('error', getMessage(notAllowedMessage, result.fail)); } if (result.pass.length > 0) { - deleteModal.open({ $emit: actionComplete }, result.pass.map(getEntity), context); + return deleteModal.open(scope, result.pass.map(getEntity), context).then(deleteResult); } } @@ -135,20 +154,5 @@ return result.context; } - function actionComplete(eventType) { - if (eventType === context.failedEvent) { - // Action failed, reload the page - $route.reload(); - } else { - // If the user is on the load balancers table then just reload the page, otherwise they - // are on the details page and we return to the table. - if (/\/load_balancer(\/)?$/.test($location.path())) { - $route.reload(); - } else { - $location.path('project/load_balancer'); - } - } - } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.spec.js index 86a5c840..76ebb6ca 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/delete/delete.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,17 +18,11 @@ 'use strict'; describe('LBaaS v2 Load Balancers Table Row Delete Service', function() { - var service, policy, modal, lbaasv2Api, $scope, $route, $location, $q, toast, items, path; + var service, policy, modal, lbaasv2Api, $scope, $location, $q, toast, items, path; function allowed(item) { spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); + var allowed = service.allowed(item); $scope.$apply(); expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'delete_loadbalancer']]}); return allowed; @@ -39,10 +34,6 @@ return def.promise; } - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(function() { @@ -75,7 +66,6 @@ lbaasv2Api = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); modal = $injector.get('horizon.framework.widgets.modal.deleteModalService'); $scope = $injector.get('$rootScope').$new(); - $route = $injector.get('$route'); $location = $injector.get('$location'); $q = $injector.get('$q'); toast = $injector.get('horizon.framework.widgets.toast.service'); @@ -95,24 +85,14 @@ expect(allowed()).toBe(true); }); - it('should not allow deleting load balancers if state check fails (single)', function() { - items[0].provisioning_status = 'PENDING_UPDATE'; - expect(allowed(items[0])).toBe(false); - }); - - it('should allow batch delete even if state check fails (batch)', function() { - items[0].provisioning_status = 'PENDING_UPDATE'; - expect(allowed()).toBe(true); - }); - it('should open the delete modal', function() { - spyOn(modal, 'open'); - service.perform(items[0]); + spyOn(modal, 'open').and.callThrough(); + service.perform(items[0], $scope); $scope.$apply(); expect(modal.open.calls.count()).toBe(1); var args = modal.open.calls.argsFor(0); expect(args.length).toBe(3); - expect(args[0]).toEqual({ $emit: jasmine.any(Function) }); + expect(args[0]).toEqual($scope); expect(args[1]).toEqual([jasmine.objectContaining({ id: '1' })]); expect(args[2]).toEqual(jasmine.objectContaining({ labels: jasmine.any(Object), @@ -124,7 +104,7 @@ it('should pass function to modal that deletes load balancers', function() { spyOn(modal, 'open').and.callThrough(); spyOn(lbaasv2Api, 'deleteLoadBalancer').and.callThrough(); - service.perform(items[0]); + service.perform(items[0], $scope); $scope.$apply(); expect(lbaasv2Api.deleteLoadBalancer.calls.count()).toBe(1); expect(lbaasv2Api.deleteLoadBalancer).toHaveBeenCalledWith('1', true); @@ -135,7 +115,7 @@ spyOn(toast, 'add'); items[0].provisioning_status = 'PENDING_UPDATE'; items[1].provisioning_status = 'PENDING_DELETE'; - service.perform(items); + service.perform(items, $scope); $scope.$apply(); expect(modal.open).not.toHaveBeenCalled(); expect(toast.add).toHaveBeenCalledWith('error', @@ -147,7 +127,7 @@ spyOn(lbaasv2Api, 'deleteLoadBalancer').and.returnValue(makePromise(true)); spyOn(toast, 'add'); items.splice(1, 1); - service.perform(items); + service.perform(items, $scope); $scope.$apply(); expect(modal.open).toHaveBeenCalled(); expect(lbaasv2Api.deleteLoadBalancer.calls.count()).toBe(1); @@ -155,19 +135,11 @@ 'be deleted, possibly due to existing listeners: First.'); }); - it('should reload table after delete', function() { - path = 'project/load_balancer'; - spyOn($route, 'reload'); - service.perform(items); - $scope.$apply(); - expect($route.reload).toHaveBeenCalled(); - }); - - it('should return to table after delete if on detail page', function() { + it('should return to panel after delete if on detail page', function() { path = 'project/load_balancer/1'; spyOn($location, 'path'); spyOn(toast, 'add'); - service.perform(items[0]); + service.perform(items[0], $scope); $scope.$apply(); expect($location.path).toHaveBeenCalledWith('project/load_balancer'); expect(toast.add).toHaveBeenCalledWith('success', 'Deleted load balancers: First.'); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.js index 23370121..4097927d 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ angular .module('horizon.dashboard.project.lbaasv2.loadbalancers') .factory( - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip', modalService); modalService.$inject = [ @@ -34,11 +35,13 @@ /** * @ngDoc factory - * @name horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service + * @name horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip + * * @description * Brings up the disassociate floating IP confirmation modal dialog. * On submit, dsiassociates the floating IP address from the load balancer. * On cancel, does nothing. + * * @param $q The angular service for promises. * @param $route The angular $route service. * @param deleteModal The horizon delete modal service. @@ -46,7 +49,8 @@ * @param policy The horizon policy service. * @param qExtensions Horizon extensions to the $q service. * @param gettext The horizon gettext function for translation. - * @returns The load balancers table row delete service. + * + * @returns The Disassociate Floating IP modal service. */ function modalService($q, $route, deleteModal, network, policy, qExtensions, gettext) { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.spec.js index 8c07d3b9..45db4936 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/disassociate-ip/modal.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -34,10 +35,6 @@ return allowed; } - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(function() { @@ -71,7 +68,7 @@ $scope = $injector.get('$rootScope').$new(); $route = $injector.get('$route'); service = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service'); + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip'); })); it('should have the "allowed" and "perform" functions', function() { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.js new file mode 100644 index 00000000..f8d6e918 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.js @@ -0,0 +1,77 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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.lbaasv2.loadbalancers') + .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.edit', editService); + + editService.$inject = [ + 'horizon.dashboard.project.lbaasv2.loadbalancers.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.q.extensions', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc service + * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.edit + * + * @description + * Provides the service for the edit load balancer action. + * + * @param resourceType The loadbalancer resource type. + * @param actionResultService The horizon action result service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param qExtensions Horizon extensions to the $q service. + * @param gettext The horizon gettext function for translation. + * + * @returns Edit load balancer action service. + */ + + function editService( + resourceType, actionResultService, workflowModal, policy, + qExtensions, gettext + ) { + + return workflowModal.init({ + controller: 'EditLoadBalancerWizardController', + message: gettext('The load balancer has been updated.'), + handle: handle, + allowed: allowed + }); + + /////////////// + + function allowed() { + // This rule is made up and should therefore always pass. At some point there will + // likely be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'update_loadbalancer']] }); + } + + function handle(response) { + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.loadbalancer.id) + .result; + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.spec.js new file mode 100644 index 00000000..7308e530 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/edit.service.spec.js @@ -0,0 +1,63 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Edit Load Balancer Action Service', function() { + var service, scope, policy; + + function canEdit(item) { + spyOn(policy, 'ifAllowed').and.returnValue(true); + var allowed = service.allowed(item); + scope.$apply(); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_loadbalancer']]}); + return allowed; + } + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func(); + } + } + }; + } + }); + })); + + beforeEach(inject(function ($injector) { + scope = $injector.get('$rootScope').$new(); + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.actions.edit'); + })); + + it('should allow editing an load balancer', function() { + expect(canEdit({provisioning_status: 'ACTIVE'})).toBe(true); + }); + + it('should handle the action result properly', function() { + var result = service.handle({config: {data: {loadbalancer: {id: 1}}}}); + expect(result.updated[0].id).toBe(1); + }); + + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js index 231dcf4a..46be319a 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js @@ -29,7 +29,6 @@ launchContext: { id: '1' } }; - beforeEach(module('horizon.framework.util')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(module(function ($provide) { workflowSpy = jasmine.createSpy('workflow').and.returnValue(workflow); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js deleted file mode 100644 index e8b6a8e2..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.lbaasv2.loadbalancers') - .factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions', - tableRowActions); - - tableRowActions.$inject = [ - '$q', - '$route', - 'horizon.dashboard.project.lbaasv2.workflow.modal', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.delete', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service', - 'horizon.app.core.openstack-service-api.policy', - 'horizon.app.core.openstack-service-api.network', - 'horizon.framework.util.q.extensions', - 'horizon.framework.util.i18n.gettext' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions - * - * @description - * Provides the service for the Load Balancers table row actions. - * - * @param $q The angular service for promises. - * @param $route The angular $route service. - * @param workflowModal The LBaaS workflow modal service. - * @param deleteService The load balancer delete service. - * @param associateIp The associate floating IP modal service. - * @param disassociateIp The disassociate floating IP modal service. - * @param policy The horizon policy service. - * @param network The horizon network API service. - * @param qExtensions Horizon extensions to the $q service. - * @param gettext The horizon gettext function for translation. - * @returns Load balancers table row actions service object. - */ - - function tableRowActions( - $q, - $route, - workflowModal, - deleteService, - associateIp, - disassociateIp, - policy, - network, - qExtensions, - gettext - ) { - var edit = workflowModal.init({ - controller: 'EditLoadBalancerWizardController', - message: gettext('The load balancer has been updated.'), - handle: onEdit, - allowed: canEdit - }); - - var service = { - actions: actions - }; - - return service; - - /////////////// - - function actions() { - return [{ - service: edit, - template: { - text: gettext('Edit') - } - },{ - service: associateIp, - template: { - text: gettext('Associate Floating IP') - } - },{ - service: disassociateIp, - template: { - text: gettext('Disassociate Floating IP') - } - },{ - service: deleteService, - template: { - text: gettext('Delete Load Balancer'), - type: 'delete' - } - }]; - } - - function canEdit(item) { - return $q.all([ - qExtensions.booleanAsPromise(item.provisioning_status === 'ACTIVE'), - // This rule is made up and should therefore always pass. At some point there will - // likely be a valid rule similar to this that we will want to use. - policy.ifAllowed({ rules: [['neutron', 'update_loadbalancer']] }) - ]); - } - - function onEdit(/*response*/) { - $route.reload(); - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js deleted file mode 100644 index 8cb06567..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Load Balancers Table Row Actions Service', function() { - var rowActionsService, scope, $route, actions, policy; - - function canEdit(item) { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = actions[0].service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_loadbalancer']]}); - return allowed; - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - var response = { - data: { - id: '1' - } - }; - var modal = { - open: function() { - return { - result: { - then: function(func) { - func(response); - } - } - }; - } - }; - $provide.value('$uibModal', modal); - })); - - beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $route = $injector.get('$route'); - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - rowActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions'); - actions = rowActionsService.actions(); - })); - - it('should define correct table row actions', function() { - expect(actions.length).toBe(4); - expect(actions[0].template.text).toBe('Edit'); - expect(actions[1].template.text).toBe('Associate Floating IP'); - expect(actions[2].template.text).toBe('Disassociate Floating IP'); - expect(actions[3].template.text).toBe('Delete Load Balancer'); - }); - - it('should allow editing an ACTIVE load balancer', function() { - expect(canEdit({provisioning_status: 'ACTIVE'})).toBe(true); - }); - - it('should not allow editing a non-ACTIVE load balancer', function() { - expect(canEdit({provisioning_status: 'PENDING_UPDATE'})).toBe(false); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - it('should reload table after edit', function() { - spyOn($route, 'reload').and.callThrough(); - actions[0].service.perform(); - expect($route.reload).toHaveBeenCalled(); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js deleted file mode 100644 index e01f5f5d..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2015 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.lbaasv2.loadbalancers') - .controller('LoadBalancerDetailController', LoadBalancerDetailController); - - LoadBalancerDetailController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - '$routeParams', - '$window', - '$scope' - ]; - - /** - * @ngdoc controller - * @name LoadBalancerDetailController - * - * @description - * Controller for the LBaaS v2 load balancers detail page. - * - * @param api The LBaaS v2 API service. - * @param rowActions The load balancer row actions service. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param $routeParams The angular $routeParams service. - * @param $window Angular's reference to the browser window object. - * @param $scope The angular scope object. - * @returns undefined - */ - - function LoadBalancerDetailController( - api, rowActions, loadBalancersService, $routeParams, $window, $scope - ) { - var ctrl = this; - - ctrl.loading = true; - ctrl.error = false; - ctrl.actions = rowActions.actions; - ctrl.operatingStatus = loadBalancersService.operatingStatus; - ctrl.provisioningStatus = loadBalancersService.provisioningStatus; - ctrl.listenersTabActive = $window.listenersTabActive; - - init(); - - //////////////////////////////// - - function init() { - ctrl.loadbalancer = null; - ctrl.loading = true; - ctrl.error = false; - api.getLoadBalancer($routeParams.loadbalancerId, true).then(success, fail); - } - - function success(response) { - ctrl.loadbalancer = response.data; - ctrl.loading = false; - } - - function fail(/*response*/) { - ctrl.loadbalancer = null; - ctrl.loading = false; - ctrl.error = true; - } - - // Save the active state of the listeners tab in the global window object so it can stay - // active after reloading the route following an action. - $scope.$watch(function() { - return ctrl.listenersTabActive; - }, function(active) { - $window.listenersTabActive = active; - }); - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js deleted file mode 100644 index 538f0b41..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2015 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'; - - describe('LBaaS v2 Load Balancer Detail Controller', function() { - var lbaasv2API, $scope, $window, $controller, apiFail; - - function fakeAPI() { - return { - then: function(success, fail) { - if (apiFail && fail) { - fail(); - } else { - success({ id: '1234' }); - } - } - }; - } - - function createController() { - return $controller('LoadBalancerDetailController', { - $scope: $scope, - $window: $window, - $routeParams: { loadbalancerId: '1234' } - }); - } - - /////////////////////// - - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - apiFail = false; - $provide.value('$uibModal', {}); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(fakeAPI); - $scope = $injector.get('$rootScope').$new(); - $window = {}; - $controller = $injector.get('$controller'); - })); - - it('should invoke lbaasv2 apis', function() { - createController(); - expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('1234', true); - }); - - it('should save changes to listeners tab active state', function() { - var ctrl = createController(); - expect($window.listenersTabActive).toBeUndefined(); - expect(ctrl.listenersTabActive).toBeUndefined(); - ctrl.listenersTabActive = true; - $scope.$apply(); - expect($window.listenersTabActive).toBe(true); - ctrl.listenersTabActive = false; - $scope.$apply(); - expect($window.listenersTabActive).toBe(false); - }); - - it('should set error state', function() { - apiFail = true; - var ctrl = createController(); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(true); - }); - - }); - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html deleted file mode 100644 index eccd3f22..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html +++ /dev/null @@ -1,61 +0,0 @@ -
- -
- - - -
-
-
-
Provider
-
{$ ::ctrl.loadbalancer.provider $}
-
Admin State Up
-
{$ ctrl.loadbalancer.admin_state_up | yesno $}
-
Floating IP Address
-
{$ ctrl.loadbalancer.floating_ip.ip | noValue:('None' | translate) $}
-
Load Balancer ID
-
{$ ::ctrl.loadbalancer.id $}
-
Subnet ID
-
- {$ ::ctrl.loadbalancer.vip_subnet_id $} -
-
Port ID
-
- {$ ::ctrl.loadbalancer.vip_port_id $} -
-
Created At
-
{$ ::ctrl.loadbalancer.created_at $}
-
Updated At
-
{$ ::ctrl.loadbalancer.updated_at $}
-
-
-
-
- - - -
-
-
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js new file mode 100644 index 00000000..3869046e --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js @@ -0,0 +1,96 @@ +/* + * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. + * + * 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.lbaasv2.loadbalancers') + .controller('LoadBalancerDetailController', LoadBalancerDetailController); + + LoadBalancerDetailController.$inject = [ + 'loadbalancer', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.loadbalancers.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name LoadBalancerDetailController + * + * @description + * Controller for the LBaaS v2 load balancers detail page. + * + * @param loadbalancer The loadbalancer object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The load balancer resource type. + * @param typeRegistry The horizon type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function LoadBalancerDetailController( + loadbalancer, loadBalancersService, resourceType, typeRegistry, + spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.loadbalancer = loadbalancer; + ctrl.listFunctionExtraParams = { + loadbalancerId: ctrl.loadbalancer.id + }; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.identifier = loadbalancer.id; + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.loadbalancer = response.data; + ctrl.loadbalancer.floating_ip_address = response.data.floating_ip.ip; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); + ctrl.context.loadPromise.then(loadData); + } + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js new file mode 100644 index 00000000..9e3e875a --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js @@ -0,0 +1,99 @@ +/* + * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Load Balancer Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('LoadBalancerDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing'}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data', floating_ip: {}}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + }); + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.html new file mode 100644 index 00000000..c7bf4449 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.html @@ -0,0 +1,66 @@ + + + +
+ + +
+
+ + + + + +
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/drawer.html b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/drawer.html new file mode 100644 index 00000000..c94a628b --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/drawer.html @@ -0,0 +1,9 @@ + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.js index ee5fd195..ba1690d8 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.js @@ -1,5 +1,6 @@ /* * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -26,6 +27,166 @@ */ angular - .module('horizon.dashboard.project.lbaasv2.loadbalancers', []); + .module('horizon.dashboard.project.lbaasv2.loadbalancers', []) + .constant('horizon.dashboard.project.lbaasv2.loadbalancers.resourceType', + 'OS::Octavia::LoadBalancer') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.edit', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.delete', + 'horizon.dashboard.project.lbaasv2.loadbalancers.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + associateIpService, + disassociateIpService, + deleteService, + resourceType + ) { + var loadBalancerResourceType = registry.getResourceType(resourceType); + + loadBalancerResourceType + .setNames(gettext('Load Balancer'), gettext('Load Balancers')) + .setSummaryTemplateUrl(basePath + 'loadbalancers/details/drawer.html') + .setProperties(loadBalancerProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getLoadBalancersPromise) + .setLoadFunction(loadBalancerService.getLoadBalancerPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + urlFunction: loadBalancerService.getDetailsPath + }) + .append({ + id: 'vip_address', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + loadBalancerResourceType.itemActions + .append({ + id: 'loadBalancerEdit', + service: editService, + template: { + text: gettext('Edit Load Balancer') + } + }) + .append({ + id: 'loadBalancerAssociateFloatingIp', + service: associateIpService, + template: { + text: gettext('Associate Floating IP') + } + }) + .append({ + id: 'loadBalancerDisassociateFloatingIp', + service: disassociateIpService, + template: { + text: gettext('Disassociate Floating IP') + } + }) + .append({ + id: 'loadBalancerDelete', + service: deleteService, + template: { + text: gettext('Delete Load Balancer'), + type: 'delete' + } + }); + + loadBalancerResourceType.globalActions + .append({ + id: 'loadBalancerCreate', + service: createService, + template: { + type: 'create', + text: gettext('Create Load Balancer') + } + }); + + loadBalancerResourceType.batchActions + .append({ + id: 'loadBalancerBatchDelete', + service: deleteService, + template: { + text: gettext('Delete Load Balancers'), + type: 'delete-selected' + } + }); + } + + function loadBalancerProperties(loadBalancerService) { + return { + id: gettext('ID'), + name: { + label: gettext('Name'), + filters: ['noName'] + }, + description: { + label: gettext('Description'), + filters: ['noValue'] + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + }, + vip_address: gettext('IP Address'), + vip_port_id: gettext('Port ID'), + vip_subnet_id: gettext('Subnet ID'), + vip_network_id: gettext('Network ID'), + listeners: gettext('Listeners'), + pools: gettext('Pools'), + provider: gettext('Provider'), + flavor: { + label: gettext('Flavor'), + filters: ['noValue'] + }, + floating_ip_address: { + label: gettext('Floating IP'), + filters: ['noName'] + } + }; + } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.spec.js index e48dc891..e995caeb 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2015 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -22,4 +23,48 @@ }); }); + describe('LBaaS v2 Load Balancers Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'loadBalancerEdit')).toBe(true); + expect(actionHasId(actions, 'loadBalancerAssociateFloatingIp')).toBe(true); + expect(actionHasId(actions, 'loadBalancerDisassociateFloatingIp')).toBe(true); + expect(actionHasId(actions, 'loadBalancerDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'loadBalancerCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'loadBalancerBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js index bcd5be4d..a2ec60d6 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -29,10 +30,13 @@ /** * @ngdoc service * @name horizon.dashboard.project.lbaasv2.loadbalancers.service + * * @description General service for LBaaS v2 load balancers. + * * @param $q The angular service for promises. * @param api The LBaaS V2 service API. * @param gettext The horizon gettext function for translation. + * * @returns The load balancers service. */ @@ -41,7 +45,8 @@ ONLINE: gettext('Online'), OFFLINE: gettext('Offline'), DEGRADED: gettext('Degraded'), - ERROR: gettext('Error') + ERROR: gettext('Error'), + NO_MONITOR: gettext('No Monitor') }; var provisioningStatus = { @@ -53,9 +58,37 @@ ERROR: gettext('Error') }; + var loadBalancerAlgorithm = { + ROUND_ROBIN: gettext('Round Robin'), + LEAST_CONNECTIONS: gettext('Least Connections'), + SOURCE_IP: gettext('Source IP') + }; + + var none = { + null: gettext('None') + }; + var service = { operatingStatus: operatingStatus, provisioningStatus: provisioningStatus, + loadBalancerAlgorithm: loadBalancerAlgorithm, + none: none, + nullFilter: nullFilter, + getLoadBalancersPromise: getLoadBalancersPromise, + getLoadBalancerPromise: getLoadBalancerPromise, + getDetailsPath: getDetailsPath, + getListenersPromise: getListenersPromise, + getListenerPromise: getListenerPromise, + getListenerDetailsPath: getListenerDetailsPath, + getPoolsPromise: getPoolsPromise, + getPoolPromise: getPoolPromise, + getPoolDetailsPath: getPoolDetailsPath, + getMembersPromise: getMembersPromise, + getMemberPromise: getMemberPromise, + getMemberDetailsPath: getMemberDetailsPath, + getHealthMonitorPromise: getHealthMonitorPromise, + getHealthMonitorsPromise: getHealthMonitorsPromise, + getHealthMonitorDetailsPath: getHealthMonitorDetailsPath, isActionable: isActionable }; @@ -63,6 +96,136 @@ //////////// + function nullFilter(input) { + if (none.hasOwnProperty(input)) { + return none[input]; + } + return input; + } + + function getMemberDetailsPath(item) { + return 'project/load_balancer/' + item.loadbalancerId + + '/listeners/' + item.listenerId + + '/pools/' + item.poolId + + '/members/' + item.id; + } + + function getMembersPromise(params) { + return api.getMembers(params.poolId).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.loadbalancerId = params.loadbalancerId; + item.listenerId = params.listenerId; + item.poolId = params.poolId; + return item; + } + } + } + + function getMemberPromise(poolId, memberId) { + return api.getMember(poolId, memberId); + } + + function getHealthMonitorDetailsPath(item) { + return 'project/load_balancer/' + item.loadbalancerId + + '/listeners/' + item.listenerId + + '/pools/' + item.poolId + + '/healthmonitors/' + item.id; + } + + function getHealthMonitorsPromise(params) { + return api.getHealthMonitors(params.poolId).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.loadbalancerId = params.loadbalancerId; + item.listenerId = params.listenerId; + item.poolId = params.poolId; + return item; + } + } + } + + function getHealthMonitorPromise(identifier) { + return api.getHealthMonitor(identifier); + } + + function getPoolsPromise(params) { + return api.getPools(params.loadbalancerId, params.listenerId).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.loadbalancerId = params.loadbalancerId; + item.listenerId = params.listenerId; + return item; + } + } + } + + function getPoolPromise(identifier) { + return api.getPool(identifier); + } + + function getPoolDetailsPath(item) { + return 'project/load_balancer/' + + item.loadbalancerId + '/listeners/' + + item.listeners[0].id + '/pools/' + item.id; + } + + function getListenersPromise(params) { + return api.getListeners(params.loadbalancerId).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.loadbalancerId = params.loadbalancerId; + return item; + } + } + } + + function getListenerPromise(identifier) { + return api.getListener(identifier); + } + + function getListenerDetailsPath(item) { + return 'project/load_balancer/' + item.loadbalancerId + '/listeners/' + item.id; + } + + function getLoadBalancersPromise() { + return api.getLoadBalancers(true).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.floating_ip_address = item.floating_ip.ip; + return item; + } + } + } + + function getLoadBalancerPromise(identifier) { + return api.getLoadBalancer(identifier, true); + } + + function getDetailsPath(item) { + return 'project/load_balancer/' + item.id; + } + /** * @ngdoc method * @name horizon.dashboard.project.lbaasv2.loadbalancers.service.isActionable diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js index 3df0b313..1892d4e9 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,32 +18,15 @@ 'use strict'; describe('LBaaS v2 Load Balancers Service', function() { - var service, $q, $scope; + var service, $q, $scope, api; - beforeEach(module('horizon.framework.widgets.toast')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); - beforeEach(module(function($provide) { - $provide.value('horizon.app.core.openstack-service-api.lbaasv2', { - getLoadBalancer: function(index) { - var loadbalancers = [{ provisioning_status: 'ACTIVE' }, - { provisioning_status: 'PENDING_UPDATE' }]; - - var deferred = $q.defer(); - deferred.resolve({ data: loadbalancers[index] }); - - return deferred.promise; - } - }); - })); - beforeEach(inject(function ($injector) { $q = $injector.get('$q'); $scope = $injector.get('$rootScope').$new(); service = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.service'); + api = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); })); it('should define value mappings', function() { @@ -50,16 +34,184 @@ expect(service.provisioningStatus).toBeDefined(); }); - it('should allow checking status of load balancer', function() { + it('should filter null property', function() { + expect(service.nullFilter('null')).toBe(gettext('None')); + expect(service.nullFilter('something else')).toBe('something else'); + }); + + it('getDetailsPath creates urls using the item\'s ID', function() { + var myItem = {id: '1234'}; + expect(service.getDetailsPath(myItem)).toBe('project/load_balancer/1234'); + }); + + it("getLoadBalancersPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getLoadBalancers').and.returnValue(deferred.promise); + var result = service.getLoadBalancersPromise({}); + deferred.resolve({data: {items: [{id: 1, updated_at: 'feb8', floating_ip: {}}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(1); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('1feb8'); + })); + + it("getLoadBalancerPromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getLoadBalancer').and.returnValue(deferred.promise); + var result = service.getLoadBalancerPromise({}); + deferred.resolve({data: {id: 1, updated_at: 'feb8', floating_ip: {}}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + + it('getListenerDetailsPath creates urls using the item\'s ID', function() { + var myItem = {loadbalancerId: '123', id: '456'}; + expect(service.getListenerDetailsPath(myItem)) + .toBe('project/load_balancer/123/listeners/456'); + }); + + it("getListenersPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getListeners').and.returnValue(deferred.promise); + var result = service.getListenersPromise({loadbalancerId: 3}); + deferred.resolve({data: {items: [{id: 1, updated_at: 'feb8'}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(1); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('1feb8'); + expect(result.$$state.value.data.items[0].loadbalancerId).toBe(3); + })); + + it("getListenerPromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getListener').and.returnValue(deferred.promise); + var result = service.getListenerPromise({loadbalancerId: 3}); + deferred.resolve({data: {id: 1, updated_at: 'feb8', floating_ip: {}}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + + it('getPoolDetailsPath creates urls using the item\'s ID', function() { + var myItem = {loadbalancerId: '123', id: '789', listeners: [{id: '456'}]}; + expect(service.getPoolDetailsPath(myItem)) + .toBe('project/load_balancer/123/listeners/456/pools/789'); + }); + + it("getPoolsPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getPools').and.returnValue(deferred.promise); + var result = service.getPoolsPromise({loadbalancerId: 3}); + deferred.resolve({data: {items: [{id: 1, updated_at: 'feb8'}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(1); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('1feb8'); + expect(result.$$state.value.data.items[0].loadbalancerId).toBe(3); + })); + + it("getPoolPromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getPool').and.returnValue(deferred.promise); + var result = service.getPoolPromise({loadbalancerId: 3}); + deferred.resolve({data: {id: 1, updated_at: 'feb8'}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + + it('getMemberDetailsPath creates urls using the item\'s ID', function() { + var myItem = { + loadbalancerId: '1', + listenerId: '2', + poolId: '3', + id: '4' + }; + expect(service.getMemberDetailsPath(myItem)) + .toBe('project/load_balancer/1/listeners/2/pools/3/members/4'); + }); + + it("getMembersPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getMembers').and.returnValue(deferred.promise); + var result = service.getMembersPromise({ + loadbalancerId: 1, + listenerId: 2, + poolId: 3 + }); + deferred.resolve({data: {items: [{id: 4, updated_at: 'feb8'}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(4); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('4feb8'); + expect(result.$$state.value.data.items[0].loadbalancerId).toBe(1); + expect(result.$$state.value.data.items[0].listenerId).toBe(2); + expect(result.$$state.value.data.items[0].poolId).toBe(3); + })); + + it("getMemberPromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getMember').and.returnValue(deferred.promise); + var result = service.getMemberPromise(2, 1); + deferred.resolve({data: {id: 1, updated_at: 'feb8'}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + + it('getHealthMonitorDetailsPath creates urls using the item\'s ID', function() { + var myItem = { + loadbalancerId: '1', + listenerId: '2', + poolId: '3', + id: '4' + }; + expect(service.getHealthMonitorDetailsPath(myItem)) + .toBe('project/load_balancer/1/listeners/2/pools/3/healthmonitors/4'); + }); + + it("getHealthMonitorsPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getHealthMonitors').and.returnValue(deferred.promise); + var result = service.getHealthMonitorsPromise({ + loadbalancerId: 1, + listenerId: 2, + poolId: 3 + }); + deferred.resolve({data: {items: [{id: 4, updated_at: 'feb8'}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(4); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('4feb8'); + expect(result.$$state.value.data.items[0].loadbalancerId).toBe(1); + expect(result.$$state.value.data.items[0].listenerId).toBe(2); + expect(result.$$state.value.data.items[0].poolId).toBe(3); + })); + + it("getHealthMonitorPromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getHealthMonitor').and.returnValue(deferred.promise); + var result = service.getHealthMonitorPromise(1); + deferred.resolve({data: {id: 1, updated_at: 'feb8'}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + + it('should allow checking active status of load balancer', function() { var active = null; + var deferred = $q.defer(); + spyOn(api, 'getLoadBalancer').and.returnValue(deferred.promise); + deferred.resolve({data: { provisioning_status: 'ACTIVE'}}); service.isActionable(0).then(function() { active = true; }); $scope.$apply(); expect(active).toBe(true); + }); - active = null; - service.isActionable(1).then(angular.noop, function() { + it('should allow checking transitional status of load balancer', function() { + var active = null; + var deferred = $q.defer(); + spyOn(api, 'getLoadBalancer').and.returnValue(deferred.promise); + deferred.resolve({data: { provisioning_status: 'PENDING_UPDATE'}}); + service.isActionable(0).then(angular.noop, function() { active = false; }); $scope.$apply(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/panel.html b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/panel.html new file mode 100644 index 00000000..d8eb16a6 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js deleted file mode 100644 index 2046fcff..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2015 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.lbaasv2.loadbalancers') - .controller('LoadBalancersTableController', LoadBalancersTableController); - - LoadBalancersTableController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service' - ]; - - /** - * @ngdoc controller - * @name LoadBalancersTableController - * - * @description - * Controller for the LBaaS v2 load balancers table. Serves as the focal point for table actions. - * - * @param api The LBaaS V2 service API. - * @param batchActions The load balancer batch actions service. - * @param rowActions The load balancer row actions service. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @returns undefined - */ - - function LoadBalancersTableController(api, batchActions, rowActions, loadBalancersService) { - - var ctrl = this; - ctrl.items = []; - ctrl.src = []; - ctrl.loading = true; - ctrl.error = false; - ctrl.checked = {}; - ctrl.batchActions = batchActions; - ctrl.rowActions = rowActions; - ctrl.operatingStatus = loadBalancersService.operatingStatus; - ctrl.provisioningStatus = loadBalancersService.provisioningStatus; - - init(); - - //////////////////////////////// - - function init() { - ctrl.src = []; - ctrl.loading = true; - api.getLoadBalancers(true).then(success, fail); - } - - function success(response) { - ctrl.src = response.data.items; - ctrl.loading = false; - } - - function fail(/*response*/) { - ctrl.src = []; - ctrl.error = true; - ctrl.loading = false; - } - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js deleted file mode 100644 index 933f6c0f..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2015 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'; - - describe('LBaaS v2 Load Balancers Table Controller', function() { - var controller, lbaasv2API, scope; - var items = [{ foo: 'bar' }]; - var apiFail = false; - - function fakeAPI() { - return { - then: function(success, fail) { - if (apiFail && fail) { - fail(); - } else { - success({ data: { items: items } }); - } - } - }; - } - - /////////////////////// - - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - $provide.value('$uibModal', {}); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - controller = $injector.get('$controller'); - scope = $injector.get('$rootScope').$new(); - spyOn(lbaasv2API, 'getLoadBalancers').and.callFake(fakeAPI); - })); - - function createController() { - return controller('LoadBalancersTableController', { $scope: scope }); - } - - it('should initialize correctly', function() { - var ctrl = createController(); - expect(ctrl.items).toEqual([]); - expect(ctrl.src).toEqual(items); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(false); - expect(ctrl.checked).toEqual({}); - expect(ctrl.batchActions).toBeDefined(); - expect(ctrl.rowActions).toBeDefined(); - expect(ctrl.operatingStatus).toBeDefined(); - expect(ctrl.provisioningStatus).toBeDefined(); - }); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getLoadBalancers).toHaveBeenCalled(); - expect(ctrl.src.length).toBe(1); - }); - - it('should show error if loading fails', function() { - apiFail = true; - var ctrl = createController(); - expect(ctrl.src.length).toBe(0); - expect(ctrl.error).toBe(true); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html deleted file mode 100644 index 03d93e30..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - NameDescriptionOperating StatusProvisioning StatusIP AddressListenersActions
- - - - - {$ ::(item.name || item.id) $}{$ ::item.description | noValue $}{$ ::item.operating_status | decode:table.operatingStatus $}{$ ::item.provisioning_status | decode:table.provisioningStatus $}{$ ::item.vip_address $}{$ item.listeners.length $} - - -
- -
- -
-
IP Address
-
{$ ::item.vip_address $}
-
-
-
Listeners
-
{$ item.listeners.length $}
-
-
-
- -
-
-
Provider
-
{$ ::item.provider $}
-
-
-
Floating IP Address
-
{$ item.floating_ip.ip || 'None' | translate $}
-
-
-
Admin State Up
-
{$ ::item.admin_state_up | yesno $}
-
-
-
ID
-
{$ ::item.id $}
-
-
-
Subnet ID
-
{$ ::item.vip_subnet_id $}
-
-
-
Port ID
-
{$ ::item.vip_port_id $}
-
-
- -
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.js deleted file mode 100644 index f8ac9e4c..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.lbaasv2.members') - .factory('horizon.dashboard.project.lbaasv2.members.actions.batchActions', - tableBatchActions); - - tableBatchActions.$inject = [ - 'horizon.framework.util.i18n.gettext', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.members.actions.update-member-list' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.pools.actions.batchActions - * - * @description - * Provides the service for the Members table batch actions. - * - * @param gettext The horizon gettext function for translation. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param updateMemberListService The LBaaS v2 update member list service. - * @returns Members table batch actions service object. - */ - - function tableBatchActions( - gettext, loadBalancersService, updateMemberListService - ) { - var loadBalancerIsActionable, loadBalancerId; - - var service = { - actions: actions, - init: init - }; - - return service; - - /////////////// - - function init(_loadBalancerId_) { - loadBalancerId = _loadBalancerId_; - loadBalancerIsActionable = loadBalancersService.isActionable(loadBalancerId); - return service; - } - - function actions() { - return [{ - service: updateMemberListService.init(loadBalancerIsActionable).update, - template: { - text: gettext('Add/Remove Pool Members') - } - }]; - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.spec.js deleted file mode 100644 index 110582d0..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/batch-actions.service.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Members Table Batch Actions Service', function() { - var actions; - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - var response = { - data: { - id: '1' - } - }; - var modal = { - open: function() { - return { - result: { - then: function(func) { - func(response); - } - } - }; - } - }; - $provide.value('$uibModal', modal); - })); - - beforeEach(inject(function ($injector) { - var batchActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.members.actions.batchActions'); - actions = batchActionsService.actions(); - })); - - it('should define correct table batch actions', function() { - expect(actions.length).toBe(1); - expect(actions[0].template.text).toBe('Add/Remove Pool Members'); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.js index fca3e7d2..09ef74cd 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.js @@ -21,9 +21,9 @@ .factory('horizon.dashboard.project.lbaasv2.members.actions.delete', deleteService); deleteService.$inject = [ - '$q', + 'horizon.dashboard.project.lbaasv2.members.resourceType', + 'horizon.framework.util.actions.action-result.service', '$location', - '$route', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.policy', @@ -33,22 +33,26 @@ /** * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.members.actions.deleteService + * * @description * Brings up the delete member confirmation modal dialog. * On submit, deletes selected member. * On cancel, does nothing. - * @param $q The angular service for promises. + * + * @param resourceType The member resource type. + * @param actionResultService The horizon action result service. * @param $location The angular $location service. - * @param $route The angular $route service. * @param deleteModal The horizon delete modal service. * @param api The LBaaS v2 API service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. + * * @returns The load balancers table delete service. */ - function deleteService($q, $location, $route, deleteModal, api, policy, gettext) { - var loadbalancerId, listenerId, poolId, statePromise; + function deleteService(resourceType, actionResultService, + $location, deleteModal, api, policy, gettext) { + var loadbalancerId, listenerId, poolId; var context = { labels: { title: gettext('Confirm Delete Member'), @@ -66,46 +70,51 @@ var service = { perform: perform, allowed: allowed, - init: init + deleteResult: deleteResult // exposed just for testing }; return service; ////////////// - function init(_loadbalancerId_, _listenerId_, _poolId_, _statePromise_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - poolId = _poolId_; - statePromise = _statePromise_; - return service; - } - - function perform(item) { - deleteModal.open({ $emit: actionComplete }, [item], context); - } - function allowed(/*item*/) { - return $q.all([ - statePromise, - // This rule is made up and should therefore always pass. I assume at some point there - // will be a valid rule similar to this that we will want to use. - policy.ifAllowed({ rules: [['neutron', 'pool_member_delete']] }) - ]); + // This rule is made up and should therefore always pass. I assume at some point there + // will be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'pool_member_delete']] }); + } + + function perform(items, scope) { + var members = angular.isArray(items) ? items : [items]; + members.map(function(item) { + loadbalancerId = item.loadbalancerId; + listenerId = item.listenerId; + poolId = item.poolId; + }); + return deleteModal.open(scope, members, context).then(deleteResult); + } + + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { + var path = 'project/load_balancer/' + loadbalancerId + + '/listeners/' + listenerId + + '/pools/' + poolId; + $location.path(path); + } + return actionResult.result; } function deleteItem(id) { return api.deleteMember(poolId, id); } - - function actionComplete(eventType) { - if (eventType === context.successEvent) { - // Success, go back to pool details page - var path = 'project/load_balancer/' + - loadbalancerId + '/listeners/' + listenerId + '/pools/' + poolId; - $location.path(path); - } - $route.reload(); - } } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.spec.js index b3864aa0..2d59fc52 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/delete/delete.action.service.spec.js @@ -17,138 +17,87 @@ 'use strict'; describe('LBaaS v2 Member Delete Service', function() { - var service, policy, modal, lbaasv2Api, $scope, $location, $q, toast, member; - - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(makePromise()); - var promise = service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - $scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'pool_member_delete']]}); - return allowed; - } - - function makePromise(reject) { - var def = $q.defer(); - def[reject ? 'reject' : 'resolve'](); - return def.promise; - } - - function isActionable(id) { - if (id === 'active') { - return $q.when(); - } else { - return $q.reject(); - } - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); + beforeEach(module('horizon.app.core')); beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module('horizon.framework')); - beforeEach(function() { - member = { id: '1', name: 'Member1' }; - }); + var deleteModalService, service, lbaasv2API, policyAPI, $location; - beforeEach(module(function($provide) { - $provide.value('$uibModal', { - open: function() { - return { - result: makePromise() - }; - } - }); - $provide.value('horizon.app.core.openstack-service-api.lbaasv2', { - deleteMember: function() { - return makePromise(); - } - }); - $provide.value('$location', { - path: function() { - return ''; - } - }); - })); - - beforeEach(inject(function ($injector) { - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - lbaasv2Api = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - modal = $injector.get('horizon.framework.widgets.modal.deleteModalService'); - $scope = $injector.get('$rootScope').$new(); - $location = $injector.get('$location'); - $q = $injector.get('$q'); - toast = $injector.get('horizon.framework.widgets.toast.service'); + beforeEach(inject(function($injector) { service = $injector.get('horizon.dashboard.project.lbaasv2.members.actions.delete'); - service.init('1', '2', '3', isActionable('active')); - $scope.$apply(); + lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + deleteModalService = $injector.get('horizon.framework.widgets.modal.deleteModalService'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + $location = $injector.get('$location'); })); - it('should have the "allowed" and "perform" functions', function() { - expect(service.allowed).toBeDefined(); - expect(service.perform).toBeDefined(); + describe('perform method', function() { + beforeEach(function () { + // just need for this to return something that looks like a promise but does nothing + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + }); + + it('should open the modal with correct label', function () { + service.perform({name: 'spam'}); + var labels = deleteModalService.open.calls.argsFor(0)[2].labels; + expect(deleteModalService.open).toHaveBeenCalled(); + angular.forEach(labels, function eachLabel(label) { + expect(label.toLowerCase()).toContain('member'); + }); + }); + + it('should open the delete modal with correct entities', function () { + service.perform([{name: 'one'}, {name: 'two'}]); + var entities = deleteModalService.open.calls.argsFor(0)[1]; + expect(deleteModalService.open).toHaveBeenCalled(); + expect(entities.length).toEqual(2); + }); + + it('should pass in a function that deletes an member', function () { + spyOn(lbaasv2API, 'deleteMember').and.callFake(angular.noop); + service.perform({poolId: 2, id: 1, name: 'one'}); + var contextArg = deleteModalService.open.calls.argsFor(0)[2]; + var deleteFunction = contextArg.deleteEntity; + deleteFunction(1); + expect(lbaasv2API.deleteMember).toHaveBeenCalledWith(2, 1); + }); }); - it('should allow deleting member from load balancer in ACTIVE state', function() { - expect(allowed()).toBe(true); - }); - - it('should not allow deleting member from load balancer in a PENDING state', function() { - service.init('1', '2', '3', isActionable('pending')); - expect(allowed()).toBe(false); - }); - - it('should open the delete modal', function() { - spyOn(modal, 'open'); - service.perform(member); - $scope.$apply(); - expect(modal.open.calls.count()).toBe(1); - var args = modal.open.calls.argsFor(0); - expect(args.length).toBe(3); - expect(args[0]).toEqual({ $emit: jasmine.any(Function) }); - expect(args[1]).toEqual([member]); - expect(args[2]).toEqual(jasmine.objectContaining({ - labels: jasmine.any(Object), - deleteEntity: jasmine.any(Function) - })); - expect(args[2].labels.title).toBe('Confirm Delete Member'); - }); - - it('should pass function to modal that deletes the member', function() { - spyOn(modal, 'open').and.callThrough(); - spyOn(lbaasv2Api, 'deleteMember').and.callThrough(); - service.perform(member); - $scope.$apply(); - expect(lbaasv2Api.deleteMember.calls.count()).toBe(1); - expect(lbaasv2Api.deleteMember).toHaveBeenCalledWith('3', '1'); - }); - - it('should show message if any items fail to be deleted', function() { - spyOn(modal, 'open').and.callThrough(); - spyOn(lbaasv2Api, 'deleteMember').and.returnValue(makePromise(true)); - spyOn(toast, 'add'); - service.perform(member); - $scope.$apply(); - expect(modal.open).toHaveBeenCalled(); - expect(lbaasv2Api.deleteMember.calls.count()).toBe(1); - expect(toast.add).toHaveBeenCalledWith('error', 'The following member could not ' + - 'be deleted: Member1.'); - }); - - it('should return to listener details after delete', function() { + it('should handle the action result properly', function() { spyOn($location, 'path'); - spyOn(toast, 'add'); - service.perform(member); - $scope.$apply(); - expect($location.path).toHaveBeenCalledWith('project/load_balancer/1/listeners/2/pools/3'); - expect(toast.add).toHaveBeenCalledWith('success', 'Deleted member: Member1.'); + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + spyOn(lbaasv2API, 'deleteMember').and.callFake(angular.noop); + service.perform({loadbalancerId: 1, listenerId: 2, poolId: 3, id: 1, name: 'one'}); + var result = service.deleteResult({ + fail: [], + pass: [{ + context: { + id: 1 + } + }] + }); + var path = 'project/load_balancer/1/listeners/2/pools/3'; + expect($location.path).toHaveBeenCalledWith(path); + expect(result.deleted[0].id).toBe(1); + result = service.deleteResult({ + pass: [], + fail: [{ + context: { + id: 1 + } + }] + }); + expect(result.failed[0].id).toBe(1); }); - }); + describe('allow method', function() { + it('should use default policy if batch action', function () { + spyOn(policyAPI, 'ifAllowed'); + service.allowed(); + expect(policyAPI.ifAllowed).toHaveBeenCalled(); + }); + }); // end of allowed + + }); // end of delete + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.js index d26c973b..32cdd4b7 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -18,13 +19,13 @@ angular .module('horizon.dashboard.project.lbaasv2.members') - .factory('horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service', + .factory('horizon.dashboard.project.lbaasv2.members.actions.edit-member', modalService); modalService.$inject = [ - '$q', + 'horizon.dashboard.project.lbaasv2.members.resourceType', + 'horizon.framework.util.actions.action-result.service', '$uibModal', - '$route', 'horizon.dashboard.project.lbaasv2.basePath', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.widgets.toast.service', @@ -38,9 +39,9 @@ * @description * Provides the service for the pool member Edit Member action. * - * @param $q The angular service for promises. + * @param resourceType The member resource type. + * @param actionResultService The horizon action result service. * @param $uibModal The angular bootstrap $uibModal service. - * @param $route The angular $route service. * @param basePath The LBaaS v2 module base path. * @param policy The horizon policy service. * @param toastService The horizon toast service. @@ -50,39 +51,29 @@ */ function modalService( - $q, + resourceType, + actionResultService, $uibModal, - $route, basePath, policy, toastService, gettext ) { - var poolId, statePromise; + var member; var service = { perform: open, - allowed: allowed, - init: init + allowed: allowed }; return service; //////////// - function init(_poolId_, _statePromise_) { - poolId = _poolId_; - statePromise = _statePromise_; - return service; - } - function allowed(/*item*/) { - return $q.all([ - statePromise, - // This rule is made up and should therefore always pass. At some point there will - // likely be a valid rule similar to this that we will want to use. - policy.ifAllowed({ rules: [['neutron', 'pool_member_update']] }) - ]); + // This rule is made up and should therefore always pass. At some point there will + // likely be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'pool_member_update']] }); } /** @@ -97,13 +88,14 @@ */ function open(item) { + member = item; var spec = { backdrop: 'static', controller: 'EditMemberModalController as modal', templateUrl: basePath + 'members/actions/edit-member/modal.html', resolve: { poolId: function() { - return poolId; + return item.poolId; }, member: function() { return item; @@ -115,8 +107,9 @@ function onModalClose() { toastService.add('success', gettext('Pool member has been updated.')); - $route.reload(); + return actionResultService.getActionResult() + .updated(resourceType, member.id) + .result; } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.spec.js index ea91cd97..e2a54a57 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/edit-member/modal.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -18,7 +19,7 @@ describe('LBaaS v2 Member Edit Service', function() { var service, policy, $scope, $route, $uibModal, toast; - var member = { id: 'member1' }; + var member = { poolId:'pool1', id: 'member1' }; var fakePromise = function(response) { return { @@ -30,14 +31,7 @@ function allowed(item) { spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - $scope.$apply(); + var allowed = service.allowed(item); expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'pool_member_update']]}); return allowed; } @@ -52,7 +46,7 @@ $provide.value('$uibModal', { open: function() { return { - result: fakePromise() + result: fakePromise({config: {data: {member: {id: 1}}}}) }; } }); @@ -65,8 +59,7 @@ $route = $injector.get('$route'); $uibModal = $injector.get('$uibModal'); service = $injector.get( - 'horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service'); - service.init('pool1', fakePromise()); + 'horizon.dashboard.project.lbaasv2.members.actions.edit-member'); })); it('should have the "allowed" and "perform" functions', function() { @@ -96,13 +89,11 @@ expect(resolve.member()).toBe(member); }); - it('should show message and reload page upon closing modal', function() { + it('should show message upon closing modal', function() { spyOn(toast, 'add'); spyOn($route, 'reload'); service.perform(member); - $scope.$apply(); expect(toast.add).toHaveBeenCalledWith('success', 'Pool member has been updated.'); - expect($route.reload).toHaveBeenCalled(); }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.js deleted file mode 100644 index 750afa2c..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.lbaasv2.members') - .factory('horizon.dashboard.project.lbaasv2.members.actions.rowActions', rowActions); - - rowActions.$inject = [ - 'horizon.framework.util.i18n.gettext', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.members.actions.delete', - 'horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.members.actions.rowActions - * - * @description - * Provides the service for the pool members row actions. - * - * @param gettext The horizon gettext function for translation. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param editMember The LBaaS v2 pool member edit service. - * @returns Members row actions service object. - */ - - function rowActions(gettext, loadBalancersService, deleteService, editMember) { - var loadBalancerIsActionable, loadbalancerId, listenerId, poolId; - - var service = { - actions: actions, - init: init - }; - - return service; - - /////////////// - - function init(_loadbalancerId_, _listenerId_, _poolId_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - poolId = _poolId_; - loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId); - return service; - } - - function actions() { - return [{ - service: editMember.init(poolId, loadBalancerIsActionable), - template: { - text: gettext('Edit') - } - },{ - service: deleteService.init(loadbalancerId, listenerId, poolId, loadBalancerIsActionable), - template: { - text: gettext('Delete Member'), - type: 'delete' - } - }]; - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.spec.js deleted file mode 100644 index 6859c4ef..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/row-actions.service.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Members Row Actions Service', function() { - var actions; - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(inject(function ($injector) { - var rowActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.members.actions.rowActions'); - actions = rowActionsService.init('1', '2', '3').actions(); - var loadbalancerService = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.service'); - spyOn(loadbalancerService, 'isActionable').and.returnValue(true); - })); - - it('should define correct table row actions', function() { - expect(actions.length).toBe(2); - expect(actions[0].template.text).toBe('Edit'); - expect(actions[1].template.text).toBe('Delete Member'); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.js index 11692704..5c98fb36 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -22,8 +23,8 @@ updateMemberListService); updateMemberListService.$inject = [ - '$q', - '$route', + 'horizon.dashboard.project.lbaasv2.members.resourceType', + 'horizon.framework.util.actions.action-result.service', 'horizon.dashboard.project.lbaasv2.workflow.modal', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.i18n.gettext' @@ -31,53 +32,38 @@ /** * @ngDoc factory - * @name horizon.dashboard.project.lbaasv2.listeners.actions.updateMemberListService + * @name horizon.dashboard.project.lbaasv2.members.actions.updateMemberListService + * * @description * Provides the service for updating the list of pool members. - * @param $q The angular service for promises. - * @param $route The angular $route service. + * + * @param resourceType The member resource type. + * @param actionResultService The horizon action result service. * @param workflowModal The LBaaS workflow modal service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. + * * @returns The load balancers members update member list service. */ function updateMemberListService( - $q, $route, workflowModal, policy, gettext + resourceType, actionResultService, workflowModal, policy, gettext ) { - var statePromise; - - var updateList = workflowModal.init({ + return workflowModal.init({ controller: 'UpdateMemberListWizardController', message: gettext('The pool members have been updated.'), - handle: onUpdate, + handle: handle, allowed: allowed }); - var service = { - init: init, - update: updateList - }; - - return service; - - ////////////// - - function init(_statePromise_) { - statePromise = _statePromise_; - return service; - } - function allowed(/*item*/) { - return $q.all([ - statePromise, - policy.ifAllowed({ rules: [['neutron', 'update_member_list']] }) - ]); + return policy.ifAllowed({ rules: [['neutron', 'update_member_list']] }); } - function onUpdate(/*response*/) { - $route.reload(); + function handle(response) { + return actionResultService.getActionResult() + .created(resourceType, response.data.id) + .result; } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.spec.js index effe9e91..4d89ed3f 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/actions/update-list/update-member-list.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,85 +18,40 @@ 'use strict'; describe('LBaaS v2 Update Member List Action Service', function() { - var scope, $q, $route, policy, init, updateMemberListService, defer; + var policy, service; - function allowed() { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = updateMemberListService.update.allowed(); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_member_list']]}); - return allowed; - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(module(function($provide) { - var response = { - data: { - id: '9012' - } - }; - var modal = { + $provide.value('$modal', { open: function() { return { result: { then: function(func) { - func(response); + func({ data: { id: 'listener1' } }); } } }; } - }; - $provide.value('$uibModal', modal); + }); })); beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - $route = $injector.get('$route'); - updateMemberListService = $injector.get( - 'horizon.dashboard.project.lbaasv2.members.actions.update-member-list'); - init = updateMemberListService.init; - defer = $q.defer(); + service = $injector.get( + 'horizon.dashboard.project.lbaasv2.members.actions.update-member-list' + ); })); - it('should define the correct service properties', function() { - expect(updateMemberListService.init).toBeDefined(); - expect(updateMemberListService.update).toBeDefined(); + it('should check policy to allow updating member list', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_member_list']]}); }); - it('should have the "allowed" and "perform" functions', function() { - expect(updateMemberListService.update.allowed).toBeDefined(); - expect(updateMemberListService.update.perform).toBeDefined(); - }); - - it('should allow editing a pool under an ACTIVE load balancer', function() { - defer.resolve(); - init(defer.promise); - expect(allowed()).toBe(true); - }); - - it('should not allow editing a pool under an NON-ACTIVE load balancer', function() { - defer.reject(); - init(defer.promise); - expect(allowed()).toBe(false); - }); - - it('should redirect after edit', function() { - spyOn($route, 'reload').and.callThrough(); - updateMemberListService.update.perform(); - expect($route.reload).toHaveBeenCalled(); + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.js deleted file mode 100644 index 3cd29a4b..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.lbaasv2.members') - .controller('MemberDetailController', MemberDetailController); - - MemberDetailController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.members.actions.rowActions', - '$routeParams', - '$q', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service' - ]; - - /** - * @ngdoc controller - * @name MemberDetailController - * - * @description - * Controller for the LBaaS v2 member detail page. - * - * @param api The LBaaS v2 API service. - * @param rowActions The pool members row actions service. - * @param $routeParams The angular $routeParams service. - * @param $q The angular service for promises. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @returns undefined - */ - - function MemberDetailController( - api, rowActions, $routeParams, $q, loadBalancersService - ) { - var ctrl = this; - - ctrl.loading = true; - ctrl.error = false; - ctrl.actions = rowActions.init($routeParams.loadbalancerId, - $routeParams.listenerId, $routeParams.poolId).actions; - ctrl.loadbalancerId = $routeParams.loadbalancerId; - ctrl.listenerId = $routeParams.listenerId; - ctrl.poolId = $routeParams.poolId; - ctrl.operatingStatus = loadBalancersService.operatingStatus; - ctrl.provisioningStatus = loadBalancersService.provisioningStatus; - - init(); - - //////////////////////////////// - - function init() { - ctrl.member = null; - ctrl.pool = null; - ctrl.listener = null; - ctrl.loadbalancer = null; - ctrl.loading = true; - ctrl.error = false; - $q.all([ - api.getMember($routeParams.poolId, $routeParams.memberId) - .then(success('member'), fail('member')), - api.getPool($routeParams.poolId) - .then(success('pool'), fail('pool')), - api.getListener($routeParams.listenerId) - .then(success('listener'), fail('listener')), - api.getLoadBalancer($routeParams.loadbalancerId) - .then(success('loadbalancer'), fail('loadbalancer')) - ]).then(postInit, initError); - } - - function success(property) { - return angular.bind(null, function setProp(property, response) { - ctrl[property] = response.data; - }, property); - } - - function fail(property) { - return angular.bind(null, function setProp(property, error) { - ctrl[property] = null; - throw error; - }, property); - } - - function postInit() { - ctrl.loading = false; - } - - function initError() { - ctrl.loading = false; - ctrl.error = true; - } - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.spec.js deleted file mode 100644 index 9acdbaec..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.controller.spec.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Member Detail Controller', function() { - var $controller, lbaasv2API, apiFail, qAllFail, actions; - - function fakePromise(data, reject) { - return { - then: function(success, fail) { - if (reject) { - fail(); - } else { - success({ data: data }); - } - return fakePromise(); - } - }; - } - - function fakeAPI() { - return fakePromise('foo', apiFail); - } - - function loadbalancerAPI() { - return fakePromise({ provisioning_status: 'ACTIVE' }); - } - - function qAll() { - return fakePromise(null, qAllFail); - } - - function createController() { - return $controller('MemberDetailController', { - $routeParams: { - loadbalancerId: 'loadbalancerId', - listenerId: 'listenerId', - poolId: 'poolId', - memberId: 'memberId' - } - }); - } - - /////////////////////// - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.widgets.toast')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - apiFail = false; - qAllFail = false; - - $provide.value('$q', { all: qAll }); - $provide.value('$uibModal', {}); - $provide.value('horizon.dashboard.project.lbaasv2.members.actions.rowActions', { - init: function() { - return { - actions: 'member-actions' - }; - } - }); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - actions = $injector.get('horizon.dashboard.project.lbaasv2.members.actions.rowActions'); - spyOn(lbaasv2API, 'getMember').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getPool').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); - spyOn(actions, 'init').and.callThrough(); - $controller = $injector.get('$controller'); - })); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getMember).toHaveBeenCalledWith('poolId','memberId'); - expect(lbaasv2API.getPool).toHaveBeenCalledWith('poolId'); - expect(lbaasv2API.getListener).toHaveBeenCalledWith('listenerId'); - expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('loadbalancerId'); - expect(ctrl.loadbalancerId).toBeDefined(); - expect(ctrl.listenerId).toBeDefined(); - expect(ctrl.poolId).toBeDefined(); - expect(ctrl.operatingStatus).toBeDefined(); - expect(ctrl.provisioningStatus).toBeDefined(); - expect(ctrl.actions).toBe('member-actions'); - expect(actions.init).toHaveBeenCalledWith('loadbalancerId', 'listenerId', 'poolId'); - }); - - it('should throw error on API fail', function() { - apiFail = true; - var init = function() { - createController(); - }; - expect(init).toThrow(); - }); - - it('should set error state if any APIs fail', function() { - qAllFail = true; - var ctrl = createController(); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(true); - }); - - }); - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.html deleted file mode 100644 index b450bcd7..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/detail.html +++ /dev/null @@ -1,48 +0,0 @@ -
- -
- -
-
-
-
Address
-
{$ ::ctrl.member.address $}
-
Admin State Up
-
{$ ctrl.member.admin_state_up | yesno $}
-
Member ID
-
{$ ::ctrl.member.id $}
-
Operating Status
-
{$ ctrl.member.operating_status | decode:ctrl.operatingStatus $}
-
Project ID
-
{$ ::ctrl.member.project_id $}
-
Protocol Port
-
{$ ::ctrl.member.protocol_port $}
-
Provisioning Status
-
{$ ctrl.member.provisioning_status | decode:ctrl.provisioningStatus $}
-
Subnet ID
-
{$ ctrl.member.subnet_id $}
-
Weight
-
{$ ctrl.member.weight $}
-
Monitor Address
-
{$ ::ctrl.member.monitor_address | noValue:('None' | translate) $}
-
Monitor Port
-
{$ ::ctrl.member.monitor_port | noValue:('None' | translate) $}
-
Created At
-
{$ ::ctrl.member.created_at $}
-
Updated At
-
{$ ::ctrl.member.updated_at $}
-
-
-
-
-
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js new file mode 100644 index 00000000..b6010bd6 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js @@ -0,0 +1,108 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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.lbaasv2.members') + .controller('MemberDetailController', MemberDetailController); + + MemberDetailController.$inject = [ + 'loadbalancer', + 'listener', + 'pool', + 'member', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.members.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name MemberDetailController + * + * @description + * Controller for the LBaaS v2 member detail page. + * + * @param loadbalancer The loadbalancer object. + * @param listener The listener object. + * @param pool The pool object. + * @param member The member object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The member resource type. + * @param typeRegistry The horizon resource type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function MemberDetailController( + loadbalancer, listener, pool, member, loadBalancersService, + resourceType, typeRegistry, spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.loadbalancer = loadbalancer; + ctrl.listener = listener; + ctrl.pool = pool; + ctrl.member = member; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.memberId = member.id; + ctrl.context.poolId = pool.id; + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.member = response.data; + ctrl.member.loadbalancerId = ctrl.loadbalancer.id; + ctrl.member.listenerId = ctrl.listener.id; + ctrl.member.poolId = ctrl.pool.id; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load( + ctrl.context.poolId, + ctrl.context.memberId + ); + ctrl.context.loadPromise.then(loadData); + } + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js new file mode 100644 index 00000000..bc7aca0a --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js @@ -0,0 +1,105 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Member Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('MemberDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + listener: { id: '123' }, + pool: { id: '123' }, + member: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + expect(ctrl.listener).toBeDefined(); + expect(ctrl.pool).toBeDefined(); + expect(ctrl.member).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing'}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + }); + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.html new file mode 100644 index 00000000..d21b4ada --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.html @@ -0,0 +1,55 @@ + +
+
+ + +
+
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/drawer.html b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/drawer.html new file mode 100644 index 00000000..05d61a7b --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/drawer.html @@ -0,0 +1,9 @@ + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.js index 0beeab5e..c1ad2606 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -26,6 +27,149 @@ */ angular - .module('horizon.dashboard.project.lbaasv2.members', []); + .module('horizon.dashboard.project.lbaasv2.members', []) + .constant('horizon.dashboard.project.lbaasv2.members.resourceType', + 'OS::Octavia::Member') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.members.actions.update-member-list', + 'horizon.dashboard.project.lbaasv2.members.actions.edit-member', + 'horizon.dashboard.project.lbaasv2.members.actions.delete', + 'horizon.dashboard.project.lbaasv2.members.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + deleteService, + resourceType + ) { + var memberResourceType = registry.getResourceType(resourceType); + + memberResourceType + .setNames(gettext('Member'), gettext('Members')) + .setSummaryTemplateUrl(basePath + 'members/details/drawer.html') + .setProperties(memberProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getMembersPromise) + .setLoadFunction(loadBalancerService.getMemberPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + urlFunction: loadBalancerService.getMemberDetailsPath + }) + .append({ + id: 'address', + priority: 1 + }) + .append({ + id: 'protocol_port', + priority: 1 + }) + .append({ + id: 'weight', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + memberResourceType.itemActions + .append({ + id: 'memberEdit', + service: editService, + template: { + text: gettext('Edit Member') + } + }) + .append({ + id: 'memberDelete', + service: deleteService, + template: { + text: gettext('Delete Member'), + type: 'delete' + } + }); + + memberResourceType.globalActions + .append({ + id: 'memberCreate', + service: createService, + template: { + type: 'create', + text: gettext('Add/Remove Members') + } + }); + + memberResourceType.batchActions + .append({ + id: 'memberBatchDelete', + service: deleteService, + template: { + text: gettext('Delete Members'), + type: 'delete-selected' + } + }); + } + + function memberProperties(loadBalancerService) { + return { + id: gettext('ID'), + name: { + label: gettext('Name'), + filters: ['noName'] + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + address: gettext('IP Address'), + protocol_port: gettext('Port'), + weight: gettext('Weight'), + subnet_id: gettext('Subnet ID'), + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + }, + monitor_address: { + label: gettext('Monitor Address'), + filters: ['noName'] + }, + monitor_port: { + label: gettext('Monitor Port'), + filters: ['noName'] + } + }; + } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.spec.js index 1806d831..e88b0644 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/members.module.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -22,4 +23,46 @@ }); }); + describe('LBaaS v2 Members Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.members.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'memberEdit')).toBe(true); + expect(actionHasId(actions, 'memberDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'memberCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'memberBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.js deleted file mode 100644 index 7f3a6160..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.lbaasv2.members') - .controller('MembersTableController', MembersTableController); - - MembersTableController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.members.actions.rowActions', - 'horizon.dashboard.project.lbaasv2.members.actions.batchActions', - '$routeParams', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service' - ]; - - /** - * @ngdoc controller - * @name MembersTableController - * - * @description - * Controller for the LBaaS v2 members table. Serves as the focal point for table actions. - * - * @param api The LBaaS V2 service API. - * @param rowActions The pool members row actions service. - * @param batchActions The members batch actions service. - * @param $routeParams The angular $routeParams service. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @returns undefined - */ - - function MembersTableController( - api, rowActions, batchActions, $routeParams, loadBalancersService - ) { - var ctrl = this; - ctrl.items = []; - ctrl.src = []; - ctrl.loading = true; - ctrl.error = false; - ctrl.checked = {}; - ctrl.loadbalancerId = $routeParams.loadbalancerId; - ctrl.listenerId = $routeParams.listenerId; - ctrl.poolId = $routeParams.poolId; - ctrl.rowActions = rowActions.init(ctrl.loadbalancerId, ctrl.listenerId, ctrl.poolId); - ctrl.batchActions = batchActions.init(ctrl.loadbalancerId); - ctrl.operatingStatus = loadBalancersService.operatingStatus; - ctrl.provisioningStatus = loadBalancersService.provisioningStatus; - - init(); - - //////////////////////////////// - - function init() { - ctrl.src = []; - ctrl.loading = true; - ctrl.error = false; - api.getMembers(ctrl.poolId).then(success, fail); - } - - function success(response) { - ctrl.src = response.data.items; - ctrl.loading = false; - } - - function fail(/*response*/) { - ctrl.src = []; - ctrl.loading = false; - ctrl.error = true; - } - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.spec.js deleted file mode 100644 index 37b59999..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.controller.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2016 IBM Corp. - * Copyright 2017 Walmart. - * - * 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('LBaaS v2 Members Table Controller', function() { - var controller, lbaasv2API, scope, actions; - var items = [{ foo: 'bar' }]; - var apiFail = false; - - function fakeAPI() { - return { - then: function(success, fail) { - if (apiFail && fail) { - fail(); - } else { - success({ data: { items: items } }); - } - } - }; - } - - /////////////////////// - - beforeEach(module('horizon.framework.widgets.toast')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - beforeEach(module(function($provide) { - $provide.value('$uibModal', {}); - $provide.value('horizon.dashboard.project.lbaasv2.members.actions.rowActions', { - init: function() { - return { - actions: 'member-actions' - }; - } - }); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - actions = $injector.get('horizon.dashboard.project.lbaasv2.members.actions.rowActions'); - controller = $injector.get('$controller'); - spyOn(lbaasv2API, 'getMembers').and.callFake(fakeAPI); - spyOn(actions, 'init').and.callThrough(); - })); - - function createController() { - return controller('MembersTableController', { - $scope: scope, - $routeParams: { - loadbalancerId: 'loadbalancerId', - listenerId: 'listenerId', - poolId: 'poolId' - }}); - } - - it('should initialize the controller properties correctly', function() { - var ctrl = createController(); - expect(ctrl.items).toEqual([]); - expect(ctrl.src).toEqual(items); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(false); - expect(ctrl.checked).toEqual({}); - expect(ctrl.loadbalancerId).toBeDefined(); - expect(ctrl.listenerId).toBeDefined(); - expect(ctrl.poolId).toBeDefined(); - expect(ctrl.rowActions).toBeDefined(); - expect(ctrl.batchActions).toBeDefined(); - expect(ctrl.operatingStatus).toBeDefined(); - expect(ctrl.provisioningStatus).toBeDefined(); - }); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getMembers).toHaveBeenCalled(); - expect(ctrl.src.length).toBe(1); - }); - - it('should show error if loading fails', function() { - apiFail = true; - var ctrl = createController(); - expect(ctrl.src.length).toBe(0); - expect(ctrl.error).toBe(true); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.html b/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.html deleted file mode 100644 index a948613b..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/table.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - IDIP AddressProtocol PortOperating StatusProvisioning StatusWeightActions
- - - - - {$ ::item.id $}{$ ::item.address $}{$ ::item.protocol_port $}{$ ::item.operating_status | decode:table.operatingStatus $}{$ ::item.provisioning_status | decode:table.provisioningStatus $}{$ item.weight $} - - -
- -
-
-
Monitor Address
-
{$ ::item.monitor_address | noValue:('None' | translate) $}
-
-
-
Monitor Port
-
{$ ::item.monitor_port | noValue:('None' | translate) $}
-
-
- -
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js index 4174c461..ba1b5eb9 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,69 +22,59 @@ .factory('horizon.dashboard.project.lbaasv2.pools.actions.create', createService); createService.$inject = [ + 'horizon.dashboard.project.lbaasv2.pools.resourceType', + 'horizon.framework.util.actions.action-result.service', '$q', - '$location', 'horizon.dashboard.project.lbaasv2.workflow.modal', 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext', 'horizon.framework.util.q.extensions', - 'horizon.framework.util.i18n.gettext' + '$routeParams' ]; /** * @ngDoc factory - * @name horizon.dashboard.project.lbaasv2.listeners.actions.createService + * @name horizon.dashboard.project.lbaasv2.pools.actions.createService + * * @description * Provides the service for creating a pool resource. + * + * @param resourceType The pool resource type. + * @param actionResultService The horizon action result service. * @param $q The angular service for promises. - * @param $location The angular $location service. * @param workflowModal The LBaaS workflow modal service. * @param policy The horizon policy service. - * @param qExtensions Horizon extensions to the $q service. * @param gettext The horizon gettext function for translation. - * @returns The load balancers pool create service. + * @param qExtensions horizon extensions to the $q service. + * @param $routeParams The angular $routeParams service. + * + * @returns The pool create service. */ function createService( - $q, $location, workflowModal, policy, qExtensions, gettext + resourceType, actionResultService, + $q, workflowModal, policy, gettext, qExtensions, $routeParams ) { - var loadbalancerId, listenerId, statePromise; - - var create = workflowModal.init({ + return workflowModal.init({ controller: 'CreatePoolWizardController', message: gettext('A new pool is being created.'), - handle: onCreate, + handle: handle, allowed: allowed }); - var service = { - init: init, - create: create - }; - - return service; - ////////////// - function init(_loadbalancerId_, _statePromise_) { - loadbalancerId = _loadbalancerId_; - statePromise = _statePromise_; - return service; - } - - function allowed(item) { - listenerId = item.id; + function allowed() { return $q.all([ - statePromise, - qExtensions.booleanAsPromise(!item.default_pool_id), + qExtensions.booleanAsPromise(!!$routeParams.listenerId), policy.ifAllowed({ rules: [['neutron', 'create_pool']] }) ]); } - function onCreate(response) { - var poolId = response.data.id; - $location.path('project/load_balancer/' + loadbalancerId + '/listeners/' + - listenerId + '/pools/' + poolId); + function handle(response) { + return actionResultService.getActionResult() + .created(resourceType, response.data.id) + .result; } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js index d05c7dd2..ad511e1d 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,95 +18,49 @@ 'use strict'; describe('LBaaS v2 Create Pool Action Service', function() { - var scope, $q, $location, policy, init, createPoolService, defer; + var policy, service; - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = createPoolService.create.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'create_pool']]}); - return allowed; - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(module(function($provide) { - var response = { - data: { - id: '9012' - } - }; - var modal = { + $provide.value('$modal', { open: function() { return { result: { then: function(func) { - func(response); + func({ data: { id: 'listener1' } }); } } }; } - }; - $provide.value('$uibModal', modal); + }); + $provide.value('$routeParams', {}); })); beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - $location = $injector.get('$location'); - createPoolService = $injector.get( - 'horizon.dashboard.project.lbaasv2.pools.actions.create'); - init = createPoolService.init; - defer = $q.defer(); + service = $injector.get('horizon.dashboard.project.lbaasv2.pools.actions.create'); })); - it('should define the correct service properties', function() { - expect(createPoolService.init).toBeDefined(); - expect(createPoolService.create).toBeDefined(); + it('should not allow creating a pool if listenerId is not present', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + var allowed = service.allowed(); + permissionShouldFail(allowed); }); - it('should have the "allowed" and "perform" functions', function() { - expect(createPoolService.create.allowed).toBeDefined(); - expect(createPoolService.create.perform).toBeDefined(); - }); - - it('should allow creating a pool under an ACTIVE load balancer', function() { - defer.resolve(); - init('active', defer.promise); - expect(allowed({default_pool_id: ''})).toBe(true); - }); - - it('should not allow creating a pool under an NON-ACTIVE load balancer', function() { - defer.reject(); - init('non-active', defer.promise); - expect(allowed({default_pool_id: ''})).toBe(false); - }); - - it('should not allow creating a pool if a listener pool already exists', function() { - defer.resolve(); - init('active', defer.promise); - expect(allowed({default_pool_id: '1234'})).toBe(false); - }); - - it('should redirect after create', function() { - defer.resolve(); - spyOn($location, 'path').and.callThrough(); - init('1234', defer.promise).create.allowed({id: '5678'}); - createPoolService.create.perform(); - expect($location.path).toHaveBeenCalledWith( - 'project/load_balancer/1234/listeners/5678/pools/9012'); + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); }); + function permissionShouldFail(permissions) { + permissions.then( + function() { + expect(false).toBe(true); + }, + function() { + expect(true).toBe(true); + }); + } }); })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js index e1cccda0..9d0006cf 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -30,6 +31,7 @@ function CreatePoolWizardController($scope, $routeParams, model, workflowService, gettext) { var loadbalancerId = $routeParams.loadbalancerId; + var listenerId = $routeParams.listenerId; var scope = $scope; scope.model = model; scope.submit = scope.model.submit; @@ -38,7 +40,7 @@ 'fa fa-cloud-download', ['pool', 'members', 'monitor'] ); - scope.model.initialize('pool', false, loadbalancerId, scope.launchContext.id); + scope.model.initialize('pool', false, loadbalancerId, listenerId); } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.js index 62703208..dfa783e9 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,9 +22,9 @@ .factory('horizon.dashboard.project.lbaasv2.pools.actions.delete', deleteService); deleteService.$inject = [ - '$q', + 'horizon.dashboard.project.lbaasv2.pools.resourceType', + 'horizon.framework.util.actions.action-result.service', '$location', - '$route', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.policy', @@ -33,22 +34,28 @@ /** * @ngDoc factory * @name horizon.dashboard.project.lbaasv2.pools.actions.deleteService + * * @description * Brings up the delete pool confirmation modal dialog. * On submit, deletes selected pool. * On cancel, does nothing. - * @param $q The angular service for promises. + * + * @param resourceType The pool resource type. + * @param actionResultService The horizon action result service. * @param $location The angular $location service. - * @param $route The angular $route service. * @param deleteModal The horizon delete modal service. * @param api The LBaaS v2 API service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. - * @returns The load balancers table delete service. + * + * @returns The pool delete service. */ - function deleteService($q, $location, $route, deleteModal, api, policy, gettext) { - var loadbalancerId, listenerId, statePromise; + function deleteService( + resourceType, actionResultService, $location, + deleteModal, api, policy, gettext + ) { + var loadbalancerId, listenerId; var context = { labels: { title: gettext('Confirm Delete Pool'), @@ -66,47 +73,50 @@ var service = { perform: perform, allowed: allowed, - init: init + deleteResult: deleteResult // exposed just for testing }; return service; ////////////// - function init(_loadbalancerId_, _listenerId_, _statePromise_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - statePromise = _statePromise_; - return service; - } - - function perform(item) { - deleteModal.open({ $emit: actionComplete }, [item], context); - } - function allowed(/*item*/) { - return $q.all([ - statePromise, - // This rule is made up and should therefore always pass. I assume at some point there - // will be a valid rule similar to this that we will want to use. - policy.ifAllowed({ rules: [['neutron', 'delete_pool']] }) - ]); + // This rule is made up and should therefore always pass. I assume at some point there + // will be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'delete_pool']] }); + } + + function perform(items, scope) { + var pools = angular.isArray(items) ? items : [items]; + pools.map(function(item) { + loadbalancerId = item.loadbalancerId; + listenerId = item.listenerId; + }); + return deleteModal.open(scope, pools, context).then(deleteResult); + } + + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { + var path = 'project/load_balancer/' + loadbalancerId + + '/listeners/' + listenerId; + $location.path(path); + } + return actionResult.result; } function deleteItem(id) { return api.deletePool(id, true); } - function actionComplete(eventType) { - if (eventType === context.failedEvent) { - // Error, reload page - $route.reload(); - } else { - // Success, go back to listener details page - var path = 'project/load_balancer/' + loadbalancerId + '/listeners/' + listenerId; - $location.path(path); - } - } - } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.spec.js index 736b3e00..174a3a15 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/delete/delete.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,138 +18,87 @@ 'use strict'; describe('LBaaS v2 Pool Delete Service', function() { - var service, policy, modal, lbaasv2Api, $scope, $location, $q, toast, pool; - - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(makePromise()); - var promise = service.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - $scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'delete_pool']]}); - return allowed; - } - - function makePromise(reject) { - var def = $q.defer(); - def[reject ? 'reject' : 'resolve'](); - return def.promise; - } - - function isActionable(id) { - if (id === 'active') { - return $q.when(); - } else { - return $q.reject(); - } - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); + beforeEach(module('horizon.app.core')); beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module('horizon.framework')); - beforeEach(function() { - pool = { id: '1', name: 'Pool1' }; - }); + var deleteModalService, service, lbaasv2API, policyAPI, $location; - beforeEach(module(function($provide) { - $provide.value('$uibModal', { - open: function() { - return { - result: makePromise() - }; - } - }); - $provide.value('horizon.app.core.openstack-service-api.lbaasv2', { - deletePool: function() { - return makePromise(); - } - }); - $provide.value('$location', { - path: function() { - return ''; - } - }); - })); - - beforeEach(inject(function ($injector) { - policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - lbaasv2Api = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - modal = $injector.get('horizon.framework.widgets.modal.deleteModalService'); - $scope = $injector.get('$rootScope').$new(); - $location = $injector.get('$location'); - $q = $injector.get('$q'); - toast = $injector.get('horizon.framework.widgets.toast.service'); + beforeEach(inject(function($injector) { service = $injector.get('horizon.dashboard.project.lbaasv2.pools.actions.delete'); - service.init('1', '2', isActionable('active')); - $scope.$apply(); + lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + deleteModalService = $injector.get('horizon.framework.widgets.modal.deleteModalService'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + $location = $injector.get('$location'); })); - it('should have the "allowed" and "perform" functions', function() { - expect(service.allowed).toBeDefined(); - expect(service.perform).toBeDefined(); + describe('perform method', function() { + beforeEach(function () { + // just need for this to return something that looks like a promise but does nothing + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + }); + + it('should open the modal with correct label', function () { + service.perform({name: 'spam'}); + var labels = deleteModalService.open.calls.argsFor(0)[2].labels; + expect(deleteModalService.open).toHaveBeenCalled(); + angular.forEach(labels, function eachLabel(label) { + expect(label.toLowerCase()).toContain('pool'); + }); + }); + + it('should open the delete modal with correct entities', function () { + service.perform([{name: 'one'}, {name: 'two'}]); + var entities = deleteModalService.open.calls.argsFor(0)[1]; + expect(deleteModalService.open).toHaveBeenCalled(); + expect(entities.length).toEqual(2); + }); + + it('should pass in a function that deletes a pool', function () { + spyOn(lbaasv2API, 'deletePool').and.callFake(angular.noop); + service.perform({id: 1, name: 'one'}); + var contextArg = deleteModalService.open.calls.argsFor(0)[2]; + var deleteFunction = contextArg.deleteEntity; + deleteFunction(1); + expect(lbaasv2API.deletePool).toHaveBeenCalledWith(1, true); + }); }); - it('should allow deleting pool from load balancer in ACTIVE state', function() { - expect(allowed()).toBe(true); - }); - - it('should not allow deleting pool from load balancer in a PENDING state', function() { - service.init('1', '2', isActionable('pending')); - expect(allowed()).toBe(false); - }); - - it('should open the delete modal', function() { - spyOn(modal, 'open'); - service.perform(pool); - $scope.$apply(); - expect(modal.open.calls.count()).toBe(1); - var args = modal.open.calls.argsFor(0); - expect(args.length).toBe(3); - expect(args[0]).toEqual({ $emit: jasmine.any(Function) }); - expect(args[1]).toEqual([pool]); - expect(args[2]).toEqual(jasmine.objectContaining({ - labels: jasmine.any(Object), - deleteEntity: jasmine.any(Function) - })); - expect(args[2].labels.title).toBe('Confirm Delete Pool'); - }); - - it('should pass function to modal that deletes the pool', function() { - spyOn(modal, 'open').and.callThrough(); - spyOn(lbaasv2Api, 'deletePool').and.callThrough(); - service.perform(pool); - $scope.$apply(); - expect(lbaasv2Api.deletePool.calls.count()).toBe(1); - expect(lbaasv2Api.deletePool).toHaveBeenCalledWith('1', true); - }); - - it('should show message if any items fail to be deleted', function() { - spyOn(modal, 'open').and.callThrough(); - spyOn(lbaasv2Api, 'deletePool').and.returnValue(makePromise(true)); - spyOn(toast, 'add'); - service.perform(pool); - $scope.$apply(); - expect(modal.open).toHaveBeenCalled(); - expect(lbaasv2Api.deletePool.calls.count()).toBe(1); - expect(toast.add).toHaveBeenCalledWith('error', 'The following pool could not ' + - 'be deleted: Pool1.'); - }); - - it('should return to listener details after delete', function() { + it('should handle the action result properly', function() { spyOn($location, 'path'); - spyOn(toast, 'add'); - service.perform(pool); - $scope.$apply(); - expect($location.path).toHaveBeenCalledWith('project/load_balancer/1/listeners/2'); - expect(toast.add).toHaveBeenCalledWith('success', 'Deleted pool: Pool1.'); + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + spyOn(lbaasv2API, 'deletePool').and.callFake(angular.noop); + service.perform({loadbalancerId: 1, listenerId: 2, id: 1, name: 'one'}); + var result = service.deleteResult({ + fail: [], + pass: [{ + context: { + id: 1 + } + }] + }); + var path = 'project/load_balancer/1/listeners/2'; + expect($location.path).toHaveBeenCalledWith(path); + expect(result.deleted[0].id).toBe(1); + result = service.deleteResult({ + pass: [], + fail: [{ + context: { + id: 1 + } + }] + }); + expect(result.failed[0].id).toBe(1); }); - }); + describe('allow method', function() { + it('should use default policy if batch action', function () { + spyOn(policyAPI, 'ifAllowed'); + service.allowed(); + expect(policyAPI.ifAllowed).toHaveBeenCalled(); + }); + }); // end of allowed + + }); // end of delete + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.js index 40f81fb1..9b8eaed5 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -21,8 +22,8 @@ .factory('horizon.dashboard.project.lbaasv2.pools.actions.edit', editService); editService.$inject = [ - '$q', - '$route', + 'horizon.dashboard.project.lbaasv2.pools.resourceType', + 'horizon.framework.util.actions.action-result.service', 'horizon.dashboard.project.lbaasv2.workflow.modal', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.i18n.gettext' @@ -30,52 +31,39 @@ /** * @ngDoc factory - * @name horizon.dashboard.project.lbaasv2.listeners.actions.editService + * @name horizon.dashboard.project.lbaasv2.pools.actions.editService + * * @description * Provides the service for editing a pool resource. - * @param $q The angular service for promises. - * @param $route The angular $route service. + * + * @param resourceType The pool resource type. + * @param actionResultService The horizon action result service. * @param workflowModal The LBaaS workflow modal service. * @param policy The horizon policy service. * @param gettext The horizon gettext function for translation. - * @returns The load balancers pool edit service. + * + * @returns The pool edit service. */ function editService( - $q, $route, workflowModal, policy, gettext + resourceType, actionResultService, workflowModal, policy, gettext ) { - var statePromise; - var edit = workflowModal.init({ + return workflowModal.init({ controller: 'EditPoolWizardController', message: gettext('The pool has been updated.'), - handle: onEdit, + handle: handle, allowed: allowed }); - var service = { - init: init, - edit: edit - }; - - return service; - - ////////////// - - function init(_statePromise_) { - statePromise = _statePromise_; - return service; - } - function allowed(/*item*/) { - return $q.all([ - statePromise, - policy.ifAllowed({ rules: [['neutron', 'update_pool']] }) - ]); + return policy.ifAllowed({ rules: [['neutron', 'update_pool']] }); } - function onEdit(/*response*/) { - $route.reload(); + function handle(response) { + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.pool.id) + .result; } } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.spec.js index 66fceaa4..6528f2b8 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/edit/edit.action.service.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -17,85 +18,38 @@ 'use strict'; describe('LBaaS v2 Edit Pool Action Service', function() { - var scope, $q, $route, policy, init, editPoolService, defer; + var policy, service; - function allowed(item) { - spyOn(policy, 'ifAllowed').and.returnValue(true); - var promise = editPoolService.edit.allowed(item); - var allowed; - promise.then(function() { - allowed = true; - }, function() { - allowed = false; - }); - scope.$apply(); - expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_pool']]}); - return allowed; - } - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(module(function($provide) { - var response = { - data: { - id: '9012' - } - }; - var modal = { + $provide.value('$modal', { open: function() { return { result: { then: function(func) { - func(response); + func({ data: { id: 'pool1' } }); } } }; } - }; - $provide.value('$uibModal', modal); + }); })); beforeEach(inject(function ($injector) { - scope = $injector.get('$rootScope').$new(); - $q = $injector.get('$q'); policy = $injector.get('horizon.app.core.openstack-service-api.policy'); - $route = $injector.get('$route'); - editPoolService = $injector.get( - 'horizon.dashboard.project.lbaasv2.pools.actions.edit'); - init = editPoolService.init; - defer = $q.defer(); + service = $injector.get('horizon.dashboard.project.lbaasv2.pools.actions.edit'); })); - it('should define the correct service properties', function() { - expect(editPoolService.init).toBeDefined(); - expect(editPoolService.edit).toBeDefined(); + it('should check policy to allow editing a pool', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_pool']]}); }); - it('should have the "allowed" and "perform" functions', function() { - expect(editPoolService.edit.allowed).toBeDefined(); - expect(editPoolService.edit.perform).toBeDefined(); - }); - - it('should allow editing a pool under an ACTIVE load balancer', function() { - defer.resolve(); - init(defer.promise); - expect(allowed({default_pool_id: '1234'})).toBe(true); - }); - - it('should not allow editing a pool under an NON-ACTIVE load balancer', function() { - defer.reject(); - init(defer.promise); - expect(allowed({default_pool_id: '1234'})).toBe(false); - }); - - it('should redirect after edit', function() { - spyOn($route, 'reload').and.callThrough(); - editPoolService.edit.perform(); - expect($route.reload).toHaveBeenCalled(); + it('should handle the action result properly', function() { + var result = service.handle({config: {data: {pool: {id: 1}}}}); + expect(result.updated[0].id).toBe(1); }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.js deleted file mode 100644 index a4f345c4..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.lbaasv2.pools') - .factory('horizon.dashboard.project.lbaasv2.pools.actions.rowActions', - rowActions); - - rowActions.$inject = [ - 'horizon.framework.util.i18n.gettext', - 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.pools.actions.edit', - 'horizon.dashboard.project.lbaasv2.pools.actions.delete', - 'horizon.dashboard.project.lbaasv2.healthmonitors.actions.create' - ]; - - /** - * @ngdoc service - * @ngname horizon.dashboard.project.lbaasv2.pools.actions.rowActions - * - * @description - * Provides the service for the pool row actions. - * - * @param gettext The horizon gettext function for translation. - * @param loadBalancersService The LBaaS v2 load balancers service. - * @param editService The LBaaS v2 pools delete service. - * @param deleteService The LBaaS v2 pools delete service. - * @param createService The LBaaS v2 health monitor create service. - * @returns Pool row actions service object. - */ - - function rowActions(gettext, loadBalancersService, editService, deleteService, createService) { - var loadBalancerIsActionable, loadbalancerId, listenerId; - - var service = { - actions: actions, - init: init - }; - - return service; - - /////////////// - - function init(_loadbalancerId_, _listenerId_) { - loadbalancerId = _loadbalancerId_; - listenerId = _listenerId_; - loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId); - return service; - } - - function actions() { - return [{ - service: editService.init(loadBalancerIsActionable).edit, - template: { - text: gettext('Edit') - } - },{ - service: createService.init(loadbalancerId, listenerId, loadBalancerIsActionable).create, - template: { - text: gettext('Create Health Monitor') - } - },{ - service: deleteService.init(loadbalancerId, listenerId, loadBalancerIsActionable), - template: { - text: gettext('Delete Pool'), - type: 'delete' - } - }]; - } - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.spec.js deleted file mode 100644 index 382cb87b..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/actions/row-actions.service.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Pools Row Actions Service', function() { - var actions; - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(inject(function ($injector) { - var rowActionsService = $injector.get( - 'horizon.dashboard.project.lbaasv2.pools.actions.rowActions'); - actions = rowActionsService.init('1', '2').actions(); - var loadbalancerService = $injector.get( - 'horizon.dashboard.project.lbaasv2.loadbalancers.service'); - spyOn(loadbalancerService, 'isActionable').and.returnValue(true); - })); - - it('should define correct table row actions', function() { - expect(actions.length).toBe(3); - expect(actions[0].template.text).toBe('Edit'); - expect(actions[1].template.text).toBe('Create Health Monitor'); - expect(actions[2].template.text).toBe('Delete Pool'); - }); - - it('should have the "allowed" and "perform" functions', function() { - actions.forEach(function(action) { - expect(action.service.allowed).toBeDefined(); - expect(action.service.perform).toBeDefined(); - }); - }); - - }); -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.js deleted file mode 100644 index 4f0e1883..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.lbaasv2.pools') - .controller('PoolDetailController', PoolDetailController); - - PoolDetailController.$inject = [ - 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.pools.actions.rowActions', - '$routeParams', - 'horizon.framework.util.i18n.gettext', - '$window', - '$scope', - '$q' - ]; - - /** - * @ngdoc controller - * @name PoolDetailController - * - * @description - * Controller for the LBaaS v2 pool detail page. - * - * @param api The LBaaS v2 API service. - * @param rowActions The LBaaS v2 pool row actions service. - * @param $routeParams The angular $routeParams service. - * @param gettext The horizon gettext function for translation. - * @param $window Angular's reference to the browser window object. - * @param $scope The angular scope object. - * @param $q The angular service for promises. - * @returns undefined - */ - - function PoolDetailController(api, rowActions, $routeParams, gettext, $window, $scope, $q) { - var ctrl = this; - - ctrl.loading = true; - ctrl.error = false; - ctrl.loadBalancerAlgorithm = { - ROUND_ROBIN: gettext('Round Robin'), - LEAST_CONNECTIONS: gettext('Least Connections'), - SOURCE_IP: gettext('Source IP') - }; - ctrl.actions = rowActions.init($routeParams.loadbalancerId, $routeParams.listenerId).actions; - ctrl.membersTabActive = $window.membersTabActive; - - init(); - - //////////////////////////////// - - function init() { - ctrl.pool = null; - ctrl.listener = null; - ctrl.loadbalancer = null; - ctrl.loading = true; - ctrl.error = false; - $q.all([ - api.getPool($routeParams.poolId) - .then(success('pool'), fail('pool')), - api.getListener($routeParams.listenerId) - .then(success('listener'), fail('listener')), - api.getLoadBalancer($routeParams.loadbalancerId) - .then(success('loadbalancer'), fail('loadbalancer')) - ]).then(postInit, initError); - } - - function success(property) { - return angular.bind(null, function setProp(property, response) { - ctrl[property] = response.data; - }, property); - } - - function fail(property) { - return angular.bind(null, function setProp(property, error) { - ctrl[property] = null; - throw error; - }, property); - } - - function postInit() { - ctrl.loading = false; - } - - function initError() { - ctrl.loading = false; - ctrl.error = true; - } - - // Save the active state of the members tab in the global window object so it can stay - // active after reloading the route following an action. - $scope.$watch(function() { - return ctrl.membersTabActive; - }, function(active) { - $window.membersTabActive = active; - }); - - } - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.spec.js deleted file mode 100644 index 894383db..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.controller.spec.js +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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'; - - describe('LBaaS v2 Pool Detail Controller', function() { - var lbaasv2API, $scope, $window, $controller, apiFail, qAllFail; - - function fakePromise(data, reject) { - return { - then: function(success, fail) { - if (reject) { - fail(); - } else { - success({ data: data }); - } - return fakePromise(); - } - }; - } - - function fakeAPI() { - return fakePromise('foo', apiFail); - } - - function loadbalancerAPI() { - return fakePromise({ provisioning_status: 'ACTIVE' }); - } - - function qAll() { - return fakePromise(null, qAllFail); - } - - function createController() { - return $controller('PoolDetailController', { - $scope: $scope, - $window: $window, - $routeParams: { - loadbalancerId: 'loadbalancerId', - listenerId: 'listenerId', - poolId: 'poolId' - } - }); - } - - /////////////////////// - - beforeEach(module('horizon.framework.util')); - beforeEach(module('horizon.framework.widgets')); - beforeEach(module('horizon.framework.conf')); - beforeEach(module('horizon.app.core.openstack-service-api')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(module(function($provide) { - apiFail = false; - qAllFail = false; - - $provide.value('$q', { all: qAll }); - })); - - beforeEach(inject(function($injector) { - lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); - spyOn(lbaasv2API, 'getPool').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI); - spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); - $scope = $injector.get('$rootScope').$new(); - $window = {}; - $controller = $injector.get('$controller'); - })); - - it('should invoke lbaasv2 apis', function() { - var ctrl = createController(); - expect(lbaasv2API.getPool).toHaveBeenCalledWith('poolId'); - expect(lbaasv2API.getListener).toHaveBeenCalledWith('listenerId'); - expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('loadbalancerId'); - expect(ctrl.loadbalancer).toEqual({provisioning_status: 'ACTIVE'}); - expect(ctrl.listener).toBe('foo'); - expect(ctrl.pool).toBe('foo'); - }); - - it('should define mapping for the load balancer algorithm', function() { - var ctrl = createController(); - expect(ctrl.loadBalancerAlgorithm).toBeDefined(); - }); - - it('should save changes to members tab active state', function() { - var ctrl = createController(); - expect($window.membersTabActive).toBeUndefined(); - expect(ctrl.membersTabActive).toBeUndefined(); - ctrl.membersTabActive = true; - $scope.$apply(); - expect($window.membersTabActive).toBe(true); - ctrl.membersTabActive = false; - $scope.$apply(); - expect($window.membersTabActive).toBe(false); - }); - - it('should throw error on API fail', function() { - apiFail = true; - var init = function() { - createController(); - }; - expect(init).toThrow(); - }); - - it('should set error state if any APIs fail', function() { - qAllFail = true; - var ctrl = createController(); - expect(ctrl.loading).toBe(false); - expect(ctrl.error).toBe(true); - }); - - }); - -})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.html deleted file mode 100644 index 4dc88f0f..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/detail.html +++ /dev/null @@ -1,54 +0,0 @@ -
- -
- - - -
-
-
-
Protocol
-
{$ ::ctrl.pool.protocol $}
-
Load Balancer Algorithm
-
{$ ctrl.pool.lb_algorithm | decode:ctrl.loadBalancerAlgorithm $}
-
Session Persistence
-
{$ ctrl.pool.session_persistence | noValue:('None' | translate) $}
-
Admin State Up
-
{$ ctrl.pool.admin_state_up | yesno $}
-
Health Monitor ID
-
- - {$ ::ctrl.pool.health_monitor_id $} - - - {$ 'None' | translate $} - -
-
Pool ID
-
{$ ::ctrl.pool.id $}
-
Project ID
-
{$ ::ctrl.pool.project_id $}
-
Created At
-
{$ ::ctrl.pool.created_at $}
-
Updated At
-
{$ ::ctrl.pool.updated_at $}
-
-
-
-
- - - -
-
-
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js new file mode 100644 index 00000000..696a3ff5 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js @@ -0,0 +1,106 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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.lbaasv2.pools') + .controller('PoolDetailController', PoolDetailController); + + PoolDetailController.$inject = [ + 'loadbalancer', + 'listener', + 'pool', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.pools.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name PoolDetailController + * + * @description + * Controller for the LBaaS v2 pool detail page. + * + * @param loadbalancer The loadbalancer object. + * @param listener The listener object. + * @param pool The pool object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The load balancer resource type. + * @param typeRegistry The horizon type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function PoolDetailController( + loadbalancer, listener, pool, loadBalancersService, + resourceType, typeRegistry, spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.loadBalancerAlgorithm = loadBalancersService.loadBalancerAlgorithm; + ctrl.loadbalancer = loadbalancer; + ctrl.listener = listener; + ctrl.pool = pool; + ctrl.listFunctionExtraParams = { + loadbalancerId: ctrl.loadbalancer.id, + listenerId: ctrl.listener.id, + poolId: ctrl.pool.id + }; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.identifier = pool.id; + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.pool = response.data; + ctrl.pool.loadbalancerId = ctrl.loadbalancer.id; + ctrl.pool.listenerId = ctrl.listener.id; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); + ctrl.context.loadPromise.then(loadData); + } + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js new file mode 100644 index 00000000..92376332 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js @@ -0,0 +1,103 @@ +/* + * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. + * + * 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('LBaaS v2 Pool Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('PoolDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + listener: { id: '123' }, + pool: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + expect(ctrl.listener).toBeDefined(); + expect(ctrl.pool).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing'}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + }); + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html new file mode 100644 index 00000000..eaf0544e --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html @@ -0,0 +1,69 @@ + + + +
+ + +
+
+ + + + + + + + +
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/drawer.html b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/drawer.html new file mode 100644 index 00000000..fb0dab48 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/drawer.html @@ -0,0 +1,9 @@ + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js index ee56bb41..1f884b4a 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -26,6 +27,156 @@ */ angular - .module('horizon.dashboard.project.lbaasv2.pools', []); + .module('horizon.dashboard.project.lbaasv2.pools', []) + .constant('horizon.dashboard.project.lbaasv2.pools.resourceType', + 'OS::Octavia::Pool') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.pools.actions.create', + 'horizon.dashboard.project.lbaasv2.pools.actions.edit', + 'horizon.dashboard.project.lbaasv2.pools.actions.delete', + 'horizon.dashboard.project.lbaasv2.pools.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + deleteService, + resourceType + ) { + var poolResourceType = registry.getResourceType(resourceType); + + poolResourceType + .setNames(gettext('Pool'), gettext('Pools')) + .setSummaryTemplateUrl(basePath + 'pools/details/drawer.html') + .setProperties(poolProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getPoolsPromise) + .setLoadFunction(loadBalancerService.getPoolPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + urlFunction: loadBalancerService.getPoolDetailsPath + }) + .append({ + id: 'protocol', + priority: 1 + }) + .append({ + id: 'lb_algorithm', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + poolResourceType.itemActions + .append({ + id: 'poolEdit', + service: editService, + template: { + text: gettext('Edit Pool') + } + }) + .append({ + id: 'poolDelete', + service: deleteService, + template: { + text: gettext('Delete Pool'), + type: 'delete' + } + }); + + poolResourceType.globalActions + .append({ + id: 'poolCreate', + service: createService, + template: { + type: 'create', + text: gettext('Create Pool') + } + }); + + poolResourceType.batchActions + .append({ + id: 'poolBatchDelete', + service: deleteService, + template: { + text: gettext('Delete Pools'), + type: 'delete-selected' + } + }); + } + + function poolProperties(loadBalancerService) { + return { + id: gettext('ID'), + name: { + label: gettext('Name'), + filters: ['noName'] + }, + description: { + label: gettext('Description'), + filters: ['noValue'] + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + protocol: gettext('Protocol'), + lb_algorithm: { + label: gettext('Algorithm'), + values: loadBalancerService.loadBalancerAlgorithm + }, + session_persistence: { + label: gettext('Session Persistence'), + filters: [ + 'json', + loadBalancerService.nullFilter + ] + }, + health_monitor_id: { + label: gettext('Health Monitor ID'), + filters: ['noName'] + }, + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + }, + loadbalancers: gettext('Load Balancers'), + listeners: gettext('Listeners'), + members: gettext('Members') + }; + } })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.spec.js index 58497c48..4a2c226b 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.spec.js @@ -1,5 +1,6 @@ /* * Copyright 2016 IBM Corp. + * Copyright 2017 Walmart. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -22,4 +23,46 @@ }); }); + describe('LBaaS v2 Pools Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.pools.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'poolEdit')).toBe(true); + expect(actionHasId(actions, 'poolDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'poolCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'poolBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + })(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.js b/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.js deleted file mode 100644 index 24cb0f91..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.lbaasv2') - .directive('detailStatus', detailStatus); - - detailStatus.$inject = [ - 'horizon.dashboard.project.lbaasv2.basePath' - ]; - - /** - * @ngdoc directive - * @name horizon.dashboard.project.lbaasv2:detailStatus - * @description - * The `detailStatus` directive provides a status indicator while loading detail pages. It will - * show a loading indicator while the page is loading and an error indicator if there is an - * error loading the page. - * @restrict E - * - * @example - * ``` - * - * ``` - */ - - function detailStatus(basePath) { - var directive = { - restrict: 'E', - templateUrl: basePath + 'widgets/detail/detail-status.html', - scope: { - loading: '=', - error: '=' - } - }; - return directive; - } -}()); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.spec.js deleted file mode 100644 index c26c5fde..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.directive.spec.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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'; - - function digestMarkup(scope, compile, markup) { - var element = angular.element(markup); - compile(element)(scope); - scope.$apply(); - return element; - } - - describe('detailStatus directive', function() { - var $scope, $compile, markup, ctrl; - - beforeEach(module('templates')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); - - beforeEach(inject(function($injector) { - $compile = $injector.get('$compile'); - $scope = $injector.get('$rootScope').$new(); - ctrl = { - loading: true, - error: false - }; - $scope.ctrl = ctrl; - markup = ''; - })); - - it('initially shows loading status', function() { - var element = digestMarkup($scope, $compile, markup); - expect(element).toBeDefined(); - - expect(element.children().length).toBe(1); - expect(element.find('.progress-bar').hasClass('progress-bar-striped')).toBe(true); - expect(element.find('.progress-bar').hasClass('progress-bar-danger')).toBe(false); - expect(element.find('.progress-bar > span').length).toBe(1); - expect(element.find('.progress-bar > span').hasClass('sr-only')).toBe(true); - expect(element.find('.progress-bar > span').text().trim()).toBe('Loading'); - expect(element.find('.message').length).toBe(0); - }); - - it('indicates error status on error', function() { - var element = digestMarkup($scope, $compile, markup); - expect(element).toBeDefined(); - - ctrl.loading = false; - ctrl.error = true; - $scope.$apply(); - - expect(element.children().length).toBe(1); - expect(element.find('.progress-bar').hasClass('progress-bar-striped')).toBe(false); - expect(element.find('.progress-bar').hasClass('progress-bar-danger')).toBe(true); - expect(element.find('.progress-bar > span').length).toBe(1); - expect(element.find('.progress-bar > span').hasClass('sr-only')).toBe(false); - expect(element.find('.progress-bar > span').text().trim()) - .toBe('An error occurred. Please try again later.'); - expect(element.find('.error-actions').length).toBe(1); - expect(element.find('.error-actions > a').text().trim()).toBe('Back'); - }); - - it('goes away when done loading', function() { - var element = digestMarkup($scope, $compile, markup); - expect(element).toBeDefined(); - - ctrl.loading = false; - ctrl.error = false; - $scope.$apply(); - - expect(element.children().length).toBe(0); - }); - - }); -}()); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.html b/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.html deleted file mode 100644 index 3278ed1a..00000000 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/widgets/detail/detail-status.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
-
- Loading - An error occurred. Please try again later. -
-
-
- Back -
-
\ No newline at end of file diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/members/members.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/members/members.controller.spec.js index 44e33a23..ae32b55c 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/members/members.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/members/members.controller.spec.js @@ -17,9 +17,8 @@ 'use strict'; describe('Member Details Step', function() { - var model; + var model, ctrl; - beforeEach(module('horizon.framework.util.i18n')); beforeEach(module('horizon.dashboard.project.lbaasv2')); beforeEach(function() { @@ -47,128 +46,91 @@ }; }); - describe('MemberDetailsController', function() { - var ctrl; + beforeEach(inject(function($controller, $rootScope) { + var scope = $rootScope.$new(); + scope.model = model; + ctrl = $controller('MemberDetailsController', { $scope: scope }); + $.fn.popover = angular.noop; + spyOn($.fn, 'popover'); + })); - beforeEach(inject(function($controller) { - ctrl = $controller('MemberDetailsController', { $scope: { model: model } }); - })); - - it('should define error messages for invalid fields', function() { - expect(ctrl.portError).toBeDefined(); - expect(ctrl.weightError).toBeDefined(); - expect(ctrl.ipError).toBeDefined(); - }); - - it('should define patterns for validation', function() { - expect(ctrl.ipPattern).toBeDefined(); - }); - - it('should define transfer table properties', function() { - expect(ctrl.tableData).toBeDefined(); - expect(ctrl.tableLimits).toBeDefined(); - expect(ctrl.tableHelp).toBeDefined(); - }); - - it('should have available members', function() { - expect(ctrl.tableData.available).toBeDefined(); - expect(ctrl.tableData.available.length).toBe(1); - expect(ctrl.tableData.available[0].id).toBe('1'); - }); - - it('should not have allocated members', function() { - expect(ctrl.tableData.allocated).toEqual([]); - }); - - it('should allow adding multiple members', function() { - expect(ctrl.tableLimits.maxAllocation).toBe(-1); - }); - - it('should properly format address popover target', function() { - var target = ctrl.addressPopoverTarget(model.members[0]); - expect(target).toBe('1.2.3.4...'); - }); - - it('should allocate a new external member', function() { - ctrl.allocateExternalMember(); - expect(model.spec.members.length).toBe(1); - expect(model.spec.members[0].id).toBe(0); - expect(model.spec.members[0].address).toBeNull(); - expect(model.spec.members[0].subnet).toBeNull(); - }); - - it('should allocate a given member', function() { - ctrl.allocateMember(model.members[0]); - expect(model.spec.members.length).toBe(1); - expect(model.spec.members[0].id).toBe(0); - expect(model.spec.members[0].address).toEqual(model.members[0].address); - expect(model.spec.members[0].subnet).toBeUndefined(); - expect(model.spec.members[0].port).toEqual(model.members[0].port); - }); - - it('should deallocate a given member', function() { - ctrl.deallocateMember(model.spec.members[0]); - expect(model.spec.members.length).toBe(0); - }); - - it('should show subnet name for available instance', function() { - var name = ctrl.getSubnetName(model.members[0]); - expect(name).toBe('subnet-1'); - }); + it('should define error messages for invalid fields', function() { + expect(ctrl.portError).toBeDefined(); + expect(ctrl.weightError).toBeDefined(); + expect(ctrl.ipError).toBeDefined(); }); - describe('Member Details Step Template', function() { - var $scope, $element, popoverContent; + it('should define patterns for validation', function() { + expect(ctrl.ipPattern).toBeDefined(); + }); - beforeEach(module('templates')); - beforeEach(module('horizon.dashboard.project.lbaasv2')); + it('should define transfer table properties', function() { + expect(ctrl.tableData).toBeDefined(); + expect(ctrl.tableLimits).toBeDefined(); + expect(ctrl.tableHelp).toBeDefined(); + }); - beforeEach(inject(function($injector) { - var $compile = $injector.get('$compile'); - var $templateCache = $injector.get('$templateCache'); - var basePath = $injector.get('horizon.dashboard.project.lbaasv2.basePath'); - var popoverTemplates = $injector.get('horizon.dashboard.project.lbaasv2.popovers'); - var markup = $templateCache.get(basePath + 'workflow/members/members.html'); - $scope = $injector.get('$rootScope').$new(); - $scope.model = model; - $element = $compile(markup)($scope); - var popoverScope = $injector.get('$rootScope').$new(); - popoverScope.member = model.members[0]; - popoverContent = $compile(popoverTemplates.ipAddresses)(popoverScope); - })); + it('should have available members', function() { + expect(ctrl.tableData.available).toBeDefined(); + expect(ctrl.tableData.available.length).toBe(1); + expect(ctrl.tableData.available[0].id).toBe('1'); + }); - it('should show IP addresses popover on hover', function() { - var ctrl = $element.scope().ctrl; - ctrl.tableData.displayedAvailable = model.members; - $scope.$apply(); + it('should not have allocated members', function() { + expect(ctrl.tableData.allocated).toEqual([]); + }); - var popoverElement = $element.find('span.addresses-popover'); - expect(popoverElement.length).toBe(1); + it('should allow adding multiple members', function() { + expect(ctrl.tableLimits.maxAllocation).toBe(-1); + }); - $.fn.popover = angular.noop; - spyOn($.fn, 'popover'); - spyOn(ctrl, 'showAddressPopover').and.callThrough(); - popoverElement.trigger('mouseover'); + it('should properly format address popover target', function() { + var target = ctrl.addressPopoverTarget(model.members[0]); + expect(target).toBe('1.2.3.4...'); + }); - expect(ctrl.showAddressPopover).toHaveBeenCalledWith( - jasmine.objectContaining({type: 'mouseover'}), model.members[0]); - expect($.fn.popover.calls.count()).toBe(2); - expect($.fn.popover.calls.argsFor(0)[0]).toEqual({ - content: popoverContent, - html: true, - placement: 'top', - title: 'IP Addresses (2)' - }); - expect($.fn.popover.calls.argsFor(1)[0]).toBe('show'); + it('should allocate a new external member', function() { + ctrl.allocateExternalMember(); + expect(model.spec.members.length).toBe(1); + expect(model.spec.members[0].id).toBe(0); + expect(model.spec.members[0].address).toBeNull(); + expect(model.spec.members[0].subnet).toBeNull(); + }); - spyOn(ctrl, 'hideAddressPopover').and.callThrough(); - popoverElement.trigger('mouseleave'); + it('should allocate a given member', function() { + ctrl.allocateMember(model.members[0]); + expect(model.spec.members.length).toBe(1); + expect(model.spec.members[0].id).toBe(0); + expect(model.spec.members[0].address).toEqual(model.members[0].address); + expect(model.spec.members[0].subnet).toBeUndefined(); + expect(model.spec.members[0].port).toEqual(model.members[0].port); + }); - expect(ctrl.hideAddressPopover) - .toHaveBeenCalledWith(jasmine.objectContaining({type: 'mouseleave'})); - expect($.fn.popover.calls.count()).toBe(3); - expect($.fn.popover.calls.argsFor(2)[0]).toBe('hide'); + it('should deallocate a given member', function() { + ctrl.deallocateMember(model.spec.members[0]); + expect(model.spec.members.length).toBe(0); + }); + + it('should show subnet name for available instance', function() { + var name = ctrl.getSubnetName(model.members[0]); + expect(name).toBe('subnet-1'); + }); + + it('should show IP addresses popover', function() { + ctrl.showAddressPopover({ target: 'foo' }, model.members[0]); + expect($.fn.popover.calls.count()).toBe(2); + expect($.fn.popover.calls.argsFor(0)[0]).toEqual({ + content: jasmine.any(Object), + html: true, + placement: 'top', + title: 'IP Addresses (2)' }); + expect($.fn.popover.calls.argsFor(1)[0]).toBe('show'); + }); + + it('should hide IP addresses popover', function() { + ctrl.hideAddressPopover({ target: 'foo' }); + expect($.fn.popover).toHaveBeenCalledWith('hide'); }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/modal.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/modal.service.js index b8838d03..6ead2e02 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/modal.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/modal.service.js @@ -64,6 +64,7 @@ function init(args) { return { + handle: args.handle, // exposed just for testing allowed: args.allowed, perform: open }; @@ -93,13 +94,13 @@ } } }; - $uibModal.open(spec).result.then(onModalClose); + return $uibModal.open(spec).result.then(onModalClose); } function onModalClose(response) { toastService.add('success', args.message); if (args.handle) { - args.handle(response); + return args.handle(response); } } } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js index 87e7d2b4..191578a2 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js @@ -565,7 +565,7 @@ if (result.listener) { model.visibleResources.push('listener'); - model.spec.loadbalancer_id = result.listener.loadbalancers[0].id; + model.spec.loadbalancer_id = result.listener.load_balancers[0].id; if (result.listener.protocol === 'TERMINATED_HTTPS') { keymanagerPromise.then(prepareCertificates).then(function addAvailableCertificates() { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js index 64902e09..bab30f35 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js @@ -32,7 +32,7 @@ protocol: 'HTTP', protocol_port: 80, connection_limit: 999, - loadbalancers: [ { id: '1234' } ], + load_balancers: [ { id: '1234' } ], sni_container_refs: ['container2'] }, pool: {