Skip to content
This repository has been archived by the owner on Oct 20, 2021. It is now read-only.

Commit

Permalink
feat: lifecycle API and review workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
aelamrani committed May 18, 2019
1 parent a885948 commit a44eb04
Show file tree
Hide file tree
Showing 23 changed files with 479 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/components/dialog/confirm.dialog.html
Expand Up @@ -21,7 +21,7 @@ <h4>{{title}}</h4>
<p ng-bind-html="msg"></p>
</md-dialog-content>
<md-dialog-actions layout="row">
<md-button ng-click="ctrl.cancel()">Cancel</md-button>
<md-button ng-click="ctrl.cancel()">{{cancelButton}}</md-button>
<md-button class="md-raised md-primary" ng-click="ctrl.confirm()">{{confirmButton}}</md-button>
</md-dialog-actions>
</md-dialog>
3 changes: 2 additions & 1 deletion src/components/dialog/confirmDialog.controller.ts
Expand Up @@ -18,7 +18,8 @@ function DialogConfirmController($scope, $mdDialog, locals) {

$scope.title = locals.title;
$scope.msg = locals.msg;
$scope.confirmButton = locals.confirmButton;
$scope.confirmButton = locals.confirmButton || 'OK';
$scope.cancelButton = locals.cancelButton || 'Cancel';

this.cancel = function() {
$mdDialog.hide(false);
Expand Down
71 changes: 69 additions & 2 deletions src/management/api/apiAdmin.controller.ts
Expand Up @@ -37,7 +37,8 @@ class ApiAdminController {
private NotificationService: NotificationService,
private resolvedApiState: any,
private SidenavService: SidenavService,
private UserService: UserService) {
private UserService: UserService,
private Constants) {
'ngInject';

this.$scope = $scope;
Expand Down Expand Up @@ -125,7 +126,7 @@ class ApiAdminController {
template: require('../../components/dialog/confirm.dialog.html'),
clickOutsideToClose: true,
locals: {
title: 'Would you like to deploy your API ?',
title: 'Would you like to deploy your API?',
confirmButton: 'OK'
}
}).then(function (response) {
Expand All @@ -135,6 +136,34 @@ class ApiAdminController {
});
}

showReviewConfirm(ev, api) {
ev.stopPropagation();
this.$mdDialog.show({
controller: 'DialogReviewController',
controllerAs: '$ctrl',
template: require('./review/review.dialog.html'),
clickOutsideToClose: true
}).then((response) => {
if (response) {
if (response.accept) {
this.ApiService.acceptReview(api, response.message).then((response) => {
this.api.workflow_state = 'review_ok';
this.api.etag = response.headers('etag');
this.NotificationService.show(`Changes accepted for API ${this.api.name}`);
this.$rootScope.$broadcast("apiChangeSuccess", {api: this.api});
});
} else {
this.ApiService.rejectReview(api, response.message).then((response) => {
this.api.workflow_state = 'request_for_changes';
this.api.etag = response.headers('etag');
this.NotificationService.show(`Changes rejected for API ${this.api.name}`);
this.$rootScope.$broadcast("apiChangeSuccess", {api: this.api});
});
}
}
});
}

deploy(api) {
this.ApiService.deploy(api.id).then((deployedApi) => {
this.NotificationService.show("API deployed");
Expand All @@ -154,6 +183,44 @@ class ApiAdminController {
this.$rootScope.$broadcast("apiChangeSuccess", {api: this.api});
});
}

canDeploy(): boolean {
return !this.Constants.apiReview.enabled || (this.Constants.apiReview.enabled && this.api.workflow_state === 'review_ok');
}

canReview(): boolean {
return this.Constants.apiReview.enabled && this.api.workflow_state === 'in_review';
}

isRequestForChanges(): boolean {
return this.Constants.apiReview.enabled && this.api.workflow_state === 'request_for_changes';
}

isInDraft(): boolean {
return this.Constants.apiReview.enabled && this.api.workflow_state === 'draft';
}

isReviewOK(): boolean {
return this.Constants.apiReview.enabled && this.api.workflow_state === 'review_ok';
}

showRequestForChangesConfirm() {
this.$mdDialog.show({
controller: 'DialogRequestForChangesController',
controllerAs: '$ctrl',
template: require('./portal/general/dialog/requestForChanges.dialog.html'),
clickOutsideToClose: true,
}).then((response: boolean) => {
if (response) {
this.ApiService.rejectReview(this.api, response.message).then((response) => {
this.api.workflow_state = 'request_for_changes';
this.api.etag = response.headers('etag');
this.$rootScope.$broadcast("apiChangeSuccess", {api: this.api});
this.NotificationService.show(`Changes has been requested for API ${this.api.name}`);
});
}
})
}
}

export default ApiAdminController;
23 changes: 21 additions & 2 deletions src/management/api/apiAdmin.html
Expand Up @@ -17,11 +17,30 @@
-->
<section>
<div class="gravitee-api-banner" ng-class="{'gravitee-api-banner-min': reducedMode}"
ng-hide="apiCtrl.apiIsSynchronized || apiCtrl.apiJustDeployed" ng-cloak="">
ng-hide="apiCtrl.apiIsSynchronized || apiCtrl.apiJustDeployed" ng-cloak="" ng-if="apiCtrl.canDeploy()">
<div class="gravitee-api-banner-content">
API out of sync<span permission permission-only="'api-definition-u'">, <a href="" ng-click="apiCtrl.showDeployAPIConfirm($event, apiCtrl.api)">deploy your API</a></span>
</div>
</div>

<div class="gravitee-api-banner" ng-class="{'gravitee-api-banner-min': reducedMode}" ng-if="apiCtrl.canReview()" ng-cloak="">
<div class="gravitee-api-banner-content" style="background-color: #d73a49">
API in review<span permission permission-only="'api-reviews-u'">,
<a href="" ng-click="apiCtrl.showReviewConfirm($event, apiCtrl.api)">accept/reject changes</a>
</span>
</div>
</div>
<div class="gravitee-api-banner" ng-class="{'gravitee-api-banner-min': reducedMode}" ng-if="apiCtrl.isRequestForChanges()" ng-cloak="">
<div class="gravitee-api-banner-content" style="background-color: #d73a49">API reviewer requested changes</div>
</div>
<div class="gravitee-api-banner" ng-class="{'gravitee-api-banner-min': reducedMode}" ng-if="apiCtrl.isInDraft()" ng-cloak="">
<div class="gravitee-api-banner-content" style="background-color: #54a3ff">API in draft</div>
</div>
<div class="gravitee-api-banner" ng-class="{'gravitee-api-banner-min': reducedMode}" ng-if="apiCtrl.isReviewOK()" ng-cloak="">
<div class="gravitee-api-banner-content" style="background-color: #d73a49">
API review accepted<span permission permission-only="'api-reviews-u'">,
<a href="" ng-click="apiCtrl.showRequestForChangesConfirm($event, apiCtrl.api)">request for changes</a>
</span>
</div>
</div>
<div ui-view flex></div>
</section>
26 changes: 24 additions & 2 deletions src/management/api/apis.controller.ts
Expand Up @@ -106,7 +106,7 @@ export class ApisController {
if (this.query) {
promise = this.ApiService.searchApis(this.query, promOpts);
} else {
promise = this.ApiService.list(null, promOpts);
promise = this.ApiService.list(null, false, promOpts);
}

let that = this;
Expand Down Expand Up @@ -201,8 +201,30 @@ export class ApisController {
};

getQualityMetricCssClass(score) {

return this.ApiService.getQualityMetricCssClass(score);
};

getWorkflowStateLabel(api) {
switch (api.workflow_state) {
case 'draft':
return 'DRAFT';
case 'in_review':
return 'IN REVIEW';
case 'request_for_changes':
return 'NEED CHANGES';
case 'review_ok':
return '';
}
};

getWorkflowStateColor(api) {
switch (api.workflow_state) {
case 'draft':
return '#54a3ff';
case 'in_review':
case 'request_for_changes':
return '#d73a49';
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/management/api/apis.html
Expand Up @@ -66,6 +66,16 @@
<i ng-if="$ctrl.syncStatus[api.id] === undefined?false:!$ctrl.syncStatus[api.id]" class="icon-arrows-cw" style="color:#e8ac3c;font-size: 14px;">
<md-tooltip>API out of sync</md-tooltip>
</i>
<ng-md-icon ng-if="api.lifecycle_state === 'published'" icon="cloud_queue" size="17" style="top: 3px;">
<md-tooltip>Published</md-tooltip>
</ng-md-icon>
<ng-md-icon ng-if="api.lifecycle_state === 'unpublished'" icon="cloud_done" size="17" style="top: 3px;">
<md-tooltip>Unpublished</md-tooltip>
</ng-md-icon>
<span class="badge gravitee-policy-method-badge-info ng-binding ng-scope" ng-if="$ctrl.getWorkflowStateLabel(api)"
ng-style="{'background-color': $ctrl.getWorkflowStateColor(api)}">
{{$ctrl.getWorkflowStateLabel(api)}}
</span>
</td>
<td md-cell>{{api.context_path}}</td>
<td md-cell nowrap>{{api.tags.join(', ')}}</td>
Expand Down
9 changes: 7 additions & 2 deletions src/management/api/creation/steps/api-creation-step5.html
Expand Up @@ -69,14 +69,19 @@ <h5>Documentation
<ng-md-icon icon="warning" style="fill: orange;"></ng-md-icon> No documentation provided, remember to provide consumers understandable and accurate information about your API.
</p>
<h5>Confirmation</h5>
<p style="padding-top: 0px;">
<p style="padding-top: 0px;" ng-if="!$ctrl.parent.Constants.apiReview.enabled">
You can now either
<strong><md-button class="md-raised" ng-click="$ctrl.parent.createAPI(false)">create the API without deploying it</md-button></strong> letting you continue configure the API or
<strong><md-button class="md-raised" ng-click="$ctrl.parent.createAPI(true)">create and start the API</md-button></strong> to make it immediately available for the application developers.
</p>
<p style="padding-top: 0px;" ng-if="$ctrl.parent.Constants.apiReview.enabled">
You can now either
<strong><md-button class="md-raised" ng-click="$ctrl.parent.createAPI(false, false)">create the API in draft</md-button></strong> letting you continue configure the API or
<strong><md-button class="md-raised" ng-click="$ctrl.parent.createAPI(false, true)">create the API and ask for review</md-button></strong> before publishing/starting it.
</p>

<div layout="row">
<a ng-href="{{$ctrl.documentationURL}}" target="_blank" class="api-abstract-documentation-link" flex>To go further on API configuration, please consult <strong>API Management documentation</strong></a>
<a ng-href="{{$ctrl.url}}" target="_blank" class="api-abstract-documentation-link" flex>To go further on API configuration, please consult <strong>API Management documentation</strong></a>
</div>
</md-content>
</md-step-body>
Expand Down
23 changes: 18 additions & 5 deletions src/management/api/creation/steps/api-creation.controller.ts
Expand Up @@ -62,7 +62,8 @@ class ApiCreationController {
private ApiService: ApiService,
private NotificationService: NotificationService,
private $state: StateService,
private Constants: any) {
private Constants: any,
private $rootScope) {
'ngInject';
this.api = {};
this.contextPathInvalid = true;
Expand Down Expand Up @@ -190,23 +191,23 @@ class ApiCreationController {
/*
API creation
*/
createAPI(deployAndStart) {
createAPI(deployAndStart, readyForReview?: boolean) {
var alert = this.$mdDialog.confirm({
title: 'Create API ?',
content: 'The API ' + this.api.name + ' in version ' + this.api.version + ' will be created' + ((deployAndStart) ? ' and deployed.' : '.'),
ok: 'CREATE',
ok: 'CREATE' + (readyForReview? ' AND ASK FOR REVIEW':''),
cancel: 'CANCEL'
});

var that = this;
this.$mdDialog
.show(alert)
.then(function () {
that._createAPI(deployAndStart);
that._createAPI(deployAndStart, readyForReview);
});
}

_createAPI(deployAndStart) {
_createAPI(deployAndStart, readyForReview?: boolean) {
var _this = this;
// clear API pages json format
_.forEach(this.api.pages, function(page) {
Expand All @@ -224,8 +225,19 @@ class ApiCreationController {
});

// create API
if (deployAndStart) {
this.api.lifecycle_state = 'PUBLISHED';
}
this.ApiService.import(null, this.api).then(function (api) {
_this.vm.showBusyText = false;
if (readyForReview) {
_this.ApiService.askForReview(api.data).then((response) => {
api.data.workflow_state = 'in_review';
api.data.etag = response.headers('etag');
_this.api = api.data;
_this.$rootScope.$broadcast("apiChangeSuccess", {api: api.data});
});
}
if (deployAndStart) {
_this.ApiService.deploy(api.data.id).then(function() {
_this.ApiService.start(api.data).then(function() {
Expand All @@ -237,6 +249,7 @@ class ApiCreationController {
_this.NotificationService.show('API created');
_this.$state.go('management.apis.detail.portal.general', {apiId: api.data.id});
}
return api;
}).catch(function () {
_this.vm.showBusyText = false;
});
Expand Down

0 comments on commit a44eb04

Please sign in to comment.