In this tutorial you're going to implement the condition which prevents a user from closing a review when none of the reviewers have completed the review.
How to start
Implement the com.atlassian.crucible.workflow.WorkflowCondition
SPI class.
1 2@Named public class CompletedReviewerWorkflowCondition implements WorkflowCondition { public ValidationResult validateTransition(WorkflowTransitionContext transitionContext) { return ValidationResult.ok(); } }
The condition in the current state doesn't do anything useful yet. To check for some conditions during the review transition, you must implement the validateTransition
method. This method is called every time a review is being transitioned. It's up to you to verify that it's the transition type the condition should react to.
1 2public ValidationResult validateTransition(WorkflowTransitionContext transitionContext) { if (isCloseReview(transitionContext.getTransitionAction())) { return doValidate(transitionContext.getReviewId()); } return ValidationResult.ok(); } private boolean isCloseReview(String transitionAction) { return ReviewService.Action.Close.getActionString().equals(transitionAction); }
The validateTransition
method should return a ValidationResult
, which can be of one of the following states:
As soon as you defined the programmed condition to react only to closing a review you need to implement the condition check. For the purpose of this tutorial, the check verifies only if there's at least one reviewer who completed the review.
1 2private ValidationResult doValidate(PermId<ReviewData> reviewId) { if (isAtLeastOneReviewerCompleted(reviewId)) { return ValidationResult.ok(); } return ValidationResult.error(CompletedReviewerWorkflowCondition.class.getSimpleName(), new ResultMessage("No one completed the review", "This review can't be closed until at least one reviewer completes it", "This review can't be closed until at least one reviewer completes it")); } private boolean isAtLeastOneReviewerCompleted(PermId<ReviewData> reviewId) { return reviewService.getAllReviewers(reviewId).isEmpty() || !reviewService.getCompletedReviewers(reviewId).isEmpty(); }
A proper ValidationResult
can be created using static factory methods and consists of:
In previous step, you implemented CompletedReviewerWorkflowCondition
, which defines the behaviour of your condition. Now, lis the condition in atlassian-plugin.xml in workflow-condition
module to let Fisheye/Crucible's plugin system know about it.
1 2<workflow-condition key="completed-reviewers-condition" class="com.example.ampstutorial.CompletedReviewerWorkflowCondition"> </workflow-condition
The class is the class of the workflow condition, and the key are unique identifiers required by the plugin system. A single plugin can define multiple workflow-condition
modules. Once the plugin module is configured, a review can't be closed it, until at least one reviewer completes it.
Invoking the transition using REST and JAVA APIs is also prohibited and will fail with an error.
1 2$ curl -u admin:admin -X POST http://localhost:3990/fecru/rest-service/reviews-v1/CR-2/transition.json\?action\=action:closeReview { "reviewId": "CR-2", "message": "Some of workflow validation conditions have failed", "failedConditions": [ { "resultKey": "CompletedReviewerWorkflowCondition", "message": "This review can't be closed until at least one reviewer completes it", "severity":"error" } ] }
CompletedReviewerWorkflowCondition.java
1 2package com.example.ampstutorial; import com.atlassian.crucible.spi.PermId; import com.atlassian.crucible.spi.data.ReviewData; import com.atlassian.crucible.spi.services.ReviewService; import com.atlassian.crucible.workflow.ResultMessage; import com.atlassian.crucible.workflow.ValidationResult; import com.atlassian.crucible.workflow.WorkflowCondition; import com.atlassian.crucible.workflow.WorkflowTransitionContext; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import javax.inject.Inject; import javax.inject.Named; @Named public class CompletedReviewerWorkflowCondition implements WorkflowCondition { private final ReviewService reviewService; @Inject public CompletedReviewerWorkflowCondition(@ComponentImport ReviewService reviewService, @ComponentImport SoyTemplateRenderer renderer) { this.reviewService = reviewService; this.renderer = renderer; } public ValidationResult validateTransition(PermId<ReviewData> reviewId, String transitionAction, @Nullable UserData currentUser) { if (isCloseReview(transitionAction)) { return doValidate(reviewId); } return ValidationResult.ok(); } private boolean isCloseReview(String transitionAction) { return ReviewService.Action.Close.getActionString().equals(transitionAction); } private ValidationResult doValidate(PermId<ReviewData> reviewId) { if (isAtLeastOneReviewerCompleted(reviewId)) { return ValidationResult.ok(); } return ValidationResult.error(CompletedReviewerWorkflowCondition.class.getSimpleName(), new ResultMessage("No one completed the review", "This review can't be closed until at least one reviewer completes it", "This review can't be closed until at least one reviewer completes it")); } private boolean isAtLeastOneReviewerCompleted(PermId<ReviewData> reviewId) { return reviewService.getAllReviewers(reviewId).isEmpty() || !reviewService.getCompletedReviewers(reviewId).isEmpty(); } }
Rate this page: