From be9019d6c462d89e503b478a6a31384480eb1e7f Mon Sep 17 00:00:00 2001 From: brasseld Date: Sat, 12 Nov 2016 10:07:50 +0100 Subject: [PATCH] feat(plan): Manage API plan status Closes gravitee-io/issues#277 --- src/app/api/admin/plans/_apiPlans.scss | 13 +++ .../api/admin/plans/apiPlans.controller.js | 79 ++++++++++++++++++- src/app/api/admin/plans/apiPlans.html | 62 ++++++++++++--- src/app/api/admin/plans/closePlan.dialog.html | 47 +++++++++++ .../admin/plans/closePlanDialog.controller.js | 48 +++++++++++ .../api/admin/plans/publishPlan.dialog.html | 45 +++++++++++ .../plans/publishPlanDialog.controller.js | 38 +++++++++ src/app/api/portal/plan/_plan.scss | 2 +- src/app/index.module.js | 4 + src/app/services/api.service.js | 18 ++++- 10 files changed, 341 insertions(+), 15 deletions(-) create mode 100644 src/app/api/admin/plans/closePlan.dialog.html create mode 100644 src/app/api/admin/plans/closePlanDialog.controller.js create mode 100644 src/app/api/admin/plans/publishPlan.dialog.html create mode 100644 src/app/api/admin/plans/publishPlanDialog.controller.js diff --git a/src/app/api/admin/plans/_apiPlans.scss b/src/app/api/admin/plans/_apiPlans.scss index 43d669e4fd..9b95596f04 100644 --- a/src/app/api/admin/plans/_apiPlans.scss +++ b/src/app/api/admin/plans/_apiPlans.scss @@ -24,3 +24,16 @@ margin-bottom: 40px; } +.strike { + text-decoration: line-through; +} + +.italic { + font-style: italic; +} + +.md-toolbar-tools { + background: #fafafa; + color: black; +} + diff --git a/src/app/api/admin/plans/apiPlans.controller.js b/src/app/api/admin/plans/apiPlans.controller.js index 62319092a1..77b6675c80 100644 --- a/src/app/api/admin/plans/apiPlans.controller.js +++ b/src/app/api/admin/plans/apiPlans.controller.js @@ -14,19 +14,24 @@ * limitations under the License. */ class ApiPlansController { - constructor(resolvedPlans, $mdSidenav, $scope, ApiService, $stateParams, NotificationService, dragularService) { + constructor(resolvedPlans, $mdSidenav, $mdDialog, $scope, ApiService, $stateParams, NotificationService, dragularService) { 'ngInject'; this.plans = resolvedPlans.data; this.$mdSidenav = $mdSidenav; + this.$mdDialog = $mdDialog; this.$scope = $scope; this.ApiService = ApiService; this.$stateParams = $stateParams; this.NotificationService = NotificationService; this.DragularService = dragularService; + this.statusFilters = ['staging', 'published', 'closed']; + this.selectedStatus = ['staging', 'published']; + $scope.planEdit = true; this.resetPlan(); + this.applyFilters(); var that = this; $scope.configure = function (plan) { @@ -91,7 +96,33 @@ class ApiPlansController { list() { return this.ApiService.getApiPlans(this.$stateParams.apiId).then(response => { this.plans = response.data; - return {pages: this.pages}; + this.applyFilters(); + return {plans: this.plans}; + }); + } + + changeFilter(statusFilter) { + if (statusFilter === 'closed') { + this.selectedStatus.length = 0; + } else { + var idx = this.selectedStatus.indexOf('closed'); + if (idx !== -1) { + this.selectedStatus.splice(idx, 1); + } + } + + if (_.includes(this.selectedStatus, statusFilter)) { + _.pull(this.selectedStatus, statusFilter); + } else { + this.selectedStatus.push(statusFilter); + } + this.applyFilters(); + } + + applyFilters() { + var that = this; + this.filteredPlans = _.filter(this.plans, function (plan) { + return _.includes(that.selectedStatus, plan.status); }); } @@ -167,9 +198,53 @@ class ApiPlansController { that.plans = response.data; that.$mdSidenav('plan-edit').toggle(); that.$mdSidenav('live-preview').toggle(); + that.applyFilters(); + }); + }); + } + + close(plan, ev) { + var _this = this; + + this.ApiService.getPlanSubscriptions(this.$stateParams.apiId, plan.id).then(function(response) { + _this.$mdDialog.show({ + controller: 'DialogClosePlanController', + templateUrl: 'app/api/admin/plans/closePlan.dialog.html', + parent: angular.element(document.body), + targetEvent: ev, + clickOutsideToClose: true, + apiId: _this.$stateParams.apiId, + plan: plan, + subscriptions: response.data.length + }).then(function (plan) { + if (plan) { + _this.list(); + } + }, function() { + // You cancelled the dialog }); }); } + + publish(plan, ev) { + var _this = this; + + this.$mdDialog.show({ + controller: 'DialogPublishPlanController', + templateUrl: 'app/api/admin/plans/publishPlan.dialog.html', + parent: angular.element(document.body), + targetEvent: ev, + clickOutsideToClose: true, + apiId: this.$stateParams.apiId, + plan: plan + }).then(function (plan) { + if (plan) { + _this.list(); + } + }, function() { + // You cancelled the dialog + }); + } } export default ApiPlansController; diff --git a/src/app/api/admin/plans/apiPlans.html b/src/app/api/admin/plans/apiPlans.html index f37f317f24..0f78e9e837 100644 --- a/src/app/api/admin/plans/apiPlans.html +++ b/src/app/api/admin/plans/apiPlans.html @@ -15,16 +15,54 @@ limitations under the License. --> -
-
-
+ +
+ Plans + +
+ +
+
+ +
+
+
-

{{plan.name}}

- - Configure this page - - + +
+

{{plan.name}}

+ + + + + + + + + + + Close this plan + + + + Configure this plan + + + + Publish this plan + + + + +
+
@@ -46,9 +84,11 @@
{{characteristic}}
-
-
- Plans can be re-ordered by drag & dropping them. +
+ +
+ Plans can be re-ordered by dragging & dropping them. +
diff --git a/src/app/api/admin/plans/closePlan.dialog.html b/src/app/api/admin/plans/closePlan.dialog.html new file mode 100644 index 0000000000..3f28462e18 --- /dev/null +++ b/src/app/api/admin/plans/closePlan.dialog.html @@ -0,0 +1,47 @@ + + + +
+

Close plan

+ + + + +
+
+ +
+
No subscription is associated to this plan. You can delete it safely.
+
There are {{subscriptions}} subscriptions associated to this plan.
+

+ By closing this plan, all relative subscriptions will also be closed and associated api-keys will be no longer + available. +

+
+
+ + + + Cancel + + + {{subscriptions == 0 ? 'Delete' : 'Close'}} + + +
diff --git a/src/app/api/admin/plans/closePlanDialog.controller.js b/src/app/api/admin/plans/closePlanDialog.controller.js new file mode 100644 index 0000000000..ff4824904f --- /dev/null +++ b/src/app/api/admin/plans/closePlanDialog.controller.js @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * 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 DialogClosePlanController($scope, $mdDialog, ApiService, NotificationService, apiId, plan, subscriptions) { + 'ngInject'; + + $scope.apiId = apiId; + $scope.plan = plan; + $scope.subscriptions = subscriptions; + + $scope.hide = function () { + $mdDialog.cancel(); + }; + + $scope.close = function () { + if ($scope.subscriptions === 0) { + ApiService.deletePlan($scope.apiId, $scope.plan.id).then(function() { + NotificationService.show('Plan ' + plan.name + ' has been deleted'); + }).catch(function (error) { + NotificationService.show('Error while deleting plan ' + plan.name); + $scope.error = error; + }); + } else { + ApiService.closePlan($scope.apiId, $scope.plan.id).then(function() { + NotificationService.show('Plan ' + plan.name + ' has been closed'); + }).catch(function (error) { + NotificationService.show('Error while closing plan ' + plan.name); + $scope.error = error; + }); + } + + $mdDialog.hide($scope.plan); + }; +} + +export default DialogClosePlanController; diff --git a/src/app/api/admin/plans/publishPlan.dialog.html b/src/app/api/admin/plans/publishPlan.dialog.html new file mode 100644 index 0000000000..3a312bf257 --- /dev/null +++ b/src/app/api/admin/plans/publishPlan.dialog.html @@ -0,0 +1,45 @@ + + + +
+

Publish plan

+ + + + +
+
+ +
+
Would you like to publish plan {{plan.name}} ?
+

+ By publishing this plan, users will now be able to subscribe to it. +

+
+
+ + + + Cancel + + + Publish + + +
diff --git a/src/app/api/admin/plans/publishPlanDialog.controller.js b/src/app/api/admin/plans/publishPlanDialog.controller.js new file mode 100644 index 0000000000..279a05af07 --- /dev/null +++ b/src/app/api/admin/plans/publishPlanDialog.controller.js @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * 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 DialogPublishPlanController($scope, $mdDialog, ApiService, NotificationService, apiId, plan) { + 'ngInject'; + + $scope.apiId = apiId; + $scope.plan = plan; + + $scope.hide = function () { + $mdDialog.cancel(); + }; + + $scope.publish = function () { + ApiService.publishPlan($scope.apiId, $scope.plan.id).then(function() { + NotificationService.show('Plan ' + plan.name + ' has been published'); + }).catch(function (error) { + NotificationService.show('Error while publishing plan ' + plan.name); + $scope.error = error; + }); + + $mdDialog.hide($scope.plan); + }; +} + +export default DialogPublishPlanController; diff --git a/src/app/api/portal/plan/_plan.scss b/src/app/api/portal/plan/_plan.scss index dc4f8afc8d..d25c35433a 100644 --- a/src/app/api/portal/plan/_plan.scss +++ b/src/app/api/portal/plan/_plan.scss @@ -9,7 +9,7 @@ $border-radius: 10px; background-color: #fafafa; border-radius: $border-radius $border-radius 0 0; h3 { - margin: 0 auto; + margin-right: 10px; } } diff --git a/src/app/index.module.js b/src/app/index.module.js index e0e23fa90d..bc207144a2 100644 --- a/src/app/index.module.js +++ b/src/app/index.module.js @@ -105,6 +105,8 @@ import ApiPlansController from './api/admin/plans/apiPlans.controller'; import DialogSubscriptionRejectController from './api/admin/subscriptions/subscription.reject.dialog.controller'; import DialogSubscriptionAcceptController from './api/admin/subscriptions/subscription.accept.dialog.controller'; import EmptyStateDirective from './components/emptystate/emptystate.directive'; +import DialogClosePlanController from './api/admin/plans/closePlanDialog.controller'; +import DialogPublishPlanController from './api/admin/plans/publishPlanDialog.controller'; angular.module('gravitee', ['ui.router', 'ngMaterial', 'ramlConsoleApp', 'ng-showdown', 'swaggerUi', 'ngMdIcons', 'ui.codemirror', 'md.data.table', 'ngCookies', 'dragularModule', 'readMore', @@ -190,6 +192,8 @@ angular.module('gravitee', ['ui.router', 'ngMaterial', 'ramlConsoleApp', 'ng-sho .controller('ApiPlansController', ApiPlansController) .controller('DialogSubscriptionRejectController', DialogSubscriptionRejectController) .controller('DialogSubscriptionAcceptController', DialogSubscriptionAcceptController) + .controller('DialogClosePlanController', DialogClosePlanController) + .controller('DialogPublishPlanController', DialogPublishPlanController) .service('ApplicationService', ApplicationService) .service('ApiService', ApiService) .service('DocumentationService', DocumentationService) diff --git a/src/app/services/api.service.js b/src/app/services/api.service.js index d68319f4e8..9dea626a8f 100644 --- a/src/app/services/api.service.js +++ b/src/app/services/api.service.js @@ -172,7 +172,7 @@ class ApiService { * API plans */ getApiPlans(apiId) { - return this.$http.get(this.apisURL + apiId + '/plans'); + return this.$http.get(this.apisURL + apiId + '/plans?status=staging,published,closed'); } savePlan(apiId, plan) { @@ -193,6 +193,18 @@ class ApiService { } } + closePlan(apiId, planId) { + return this.$http.post(this.apisURL + apiId + '/plans/' + planId + '/_close'); + } + + deletePlan(apiId, planId) { + return this.$http.delete(this.apisURL + apiId + '/plans/' + planId); + } + + publishPlan(apiId, planId) { + return this.$http.post(this.apisURL + apiId + '/plans/' + planId + '/_publish'); + } + updatePlanSubscription(apiId, subscription) { return this.$http.put(this.apisURL + apiId + '/plans/subscriptions/' + subscription.id, subscription); } @@ -201,6 +213,10 @@ class ApiService { return this.$http.post(this.apisURL + apiId + '/plans/' + planId + '/subscriptions/' + subscriptionId + '/process', processSubscription); } + getPlanSubscriptions(apiId, planId) { + return this.$http.get(this.apisURL + apiId + '/plans/' + planId + '/subscriptions/'); + } + /* * API subscriptions */