Skip to content

Commit

Permalink
Release/v2.0.0 (#15)
Browse files Browse the repository at this point in the history
- #10 Add an ability to choose and apply the resulting estimate. (#11)
- #12 Add an ability to choose an estimation scale. Add support for the two new scales: "Classic", "T-shirt size".
- #13 Add an ability to terminate the estimation session.
- #14 Add support for the "Linear" estimation scale.
- Improve user experience by showing "estimation scale" when user click Re-Estimate;
- Replace "aui-select" with simple dropdown select.
- Update README
  • Loading branch information
aprey10 authored Mar 7, 2021
1 parent a983c12 commit 4c77eb9
Show file tree
Hide file tree
Showing 39 changed files with 1,352 additions and 132 deletions.
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,27 @@

### Actions description

**Start Estimation** - starts new estimation session for the current jira issue. The jira issue can have only one estimation session in progress.
**Start Estimation** - starts a new estimation session for the current jira issue. The jira issue can have only one estimation session in progress.

**Vote** - Submit an estimate. One user can submit only once.
**Vote** - submit an estimate. The user can estimate only once during the session.

**Stop Estimation** - stops active estimation. Only the user which started estimation can stop it.
**Stop Estimation** - stops active estimation. Only the user that started estimation can stop it.

**Re-estimate** - starts new estimation session.
**Re-estimate** - starts a new estimation session.

**Delete** - deletes all estimation sessions and estimates related to the current jira issue.
**Apply** - applies final estimate and populates 'Story Points' field (if it's present and final estimate is numeric)

**Terminate estimation** - terminates estimation session.

### Supported estimation scales

**Planning Cards** - Classic planning poker sequence: 0, 1, 2, 3, 5, 8, 13, 20, 40, 100, ?, Coffee, Infinite.

**Fibonacci** - Fibonacci sequence: 1, 2, 3, 5, 8, 13, 21, ?, Coffee, Infinite.

**Linear** - Increments in a fixed value (1): 1, 2, 3,... 12

**T-shirt size** - XS, S, M, L, XL, XXL, XXXL


### Data security and privacy statement
Expand Down
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.aprey.jira.plugin.openpoker</groupId>
<artifactId>open-poker</artifactId>
<version>1.0.1</version>
<version>2.0.0</version>
<organization>
<name>Andriy Preizner Production</name>
<url>https://www.linkedin.com/in/andrewpreizner/</url>
Expand Down Expand Up @@ -144,6 +144,12 @@
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slfj.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down Expand Up @@ -222,5 +228,6 @@
<hsql.version>1.8.0.10</hsql.version>
<slf4j-simple.version>1.6.2</slf4j-simple.version>
<ao.version>3.2.9</ao.version>
<slfj.version>1.7.30</slfj.version>
</properties>
</project>
29 changes: 29 additions & 0 deletions src/main/java/com/aprey/jira/plugin/openpoker/Deck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 Andriy Preizner
*
* This file is a part of Open Poker jira plugin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.aprey.jira.plugin.openpoker;

import java.util.List;

public interface Deck {

List<EstimationGrade> getGrades();

EstimationGrade getGrade(int gradeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@
public class Estimate {
private final ApplicationUser estimator;
private final String grade;
private final int gradeId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ public interface EstimationGrade {
int getId();

String getValue();

boolean isApplicable();
}
59 changes: 59 additions & 0 deletions src/main/java/com/aprey/jira/plugin/openpoker/EstimationScale.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2021 Andriy Preizner
*
* This file is a part of Open Poker jira plugin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.aprey.jira.plugin.openpoker;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.Getter;

@Getter
public enum EstimationScale {
CLASSIC_PLANNING("Planning Cards", EstimationUnit.CLASSIC_PLANNING),
FIBONACCI("Fibonacci", EstimationUnit.FIBONACCI),
LINEAR("Linear", EstimationUnit.LINEAR),
T_SHIRT_SIZE("T-shirt size", EstimationUnit.T_SHIRT_SIZE);

private final String name;
private EstimationUnit estimationUnit;

EstimationScale(String name, EstimationUnit estimationUnit) {
this.name = name;
this.estimationUnit = estimationUnit;
}

public static Optional<EstimationUnit> findByName(String name) {
return Arrays.stream(values())
.filter(e -> e.name.equals(name))
.map(EstimationScale::getEstimationUnit)
.findFirst();
}

public static Optional<EstimationScale> findByUnit(EstimationUnit unit) {
return Arrays.stream(values())
.filter(e -> e.estimationUnit.equals(unit))
.findFirst();
}

public static List<EstimationScale> getValues() {
return Arrays.stream(values()).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
package com.aprey.jira.plugin.openpoker;

public enum EstimationUnit {
FIBONACCI
FIBONACCI, CLASSIC_PLANNING, T_SHIRT_SIZE, LINEAR;
}
129 changes: 129 additions & 0 deletions src/main/java/com/aprey/jira/plugin/openpoker/IssueServiceFacade.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (C) 2021 Andriy Preizner
*
* This file is a part of Open Poker jira plugin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.aprey.jira.plugin.openpoker;

import com.atlassian.jira.bc.issue.IssueService;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueInputParameters;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import java.text.DecimalFormat;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Scanned
@Named
public class IssueServiceFacade {

private static final String STORY_POINT_FILED_NAME = "Story Points";

@ComponentImport
private final CustomFieldManager customFieldManager;
@ComponentImport
private final IssueService issueService;
@ComponentImport
private final IssueManager issueManager;

@Inject
public IssueServiceFacade(CustomFieldManager customFieldManager, IssueService issueService,
IssueManager issueManager) {
this.customFieldManager = customFieldManager;
this.issueService = issueService;
this.issueManager = issueManager;
}

public void applyEstimate(String estimate, ApplicationUser user, String issueId) {
Issue issue = issueManager.getIssueObject(issueId);
Optional<CustomField> field = getField(issue);
if (!field.isPresent()) {
log.error("'Story Point' custom field does not exist");
return;
}
IssueInputParameters inputParameters = buildInputParams(field.get().getIdAsLong(), estimate);
IssueService.UpdateValidationResult updateValidationResult = issueService
.validateUpdate(user, issue.getId(), inputParameters);

if (!updateValidationResult.isValid()) {
log.error("Validation for updating story point is failed, errors: {}", updateValidationResult
.getErrorCollection().toString());
return;
}

IssueService.IssueResult updateResult = issueService.update(user, updateValidationResult);
if (!updateResult.isValid()) {
log.error("ISSUE has NOT been updated. Errors: {}\n" + updateResult.getErrorCollection().toString());
return;
}

log.info("The issue {} has been updated with a new story point value", issueId);
}

private IssueInputParameters buildInputParams(long fieldId, String estimate) {
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
issueInputParameters.addCustomFieldValue(fieldId, estimate);
issueInputParameters.setSkipScreenCheck(true);
issueInputParameters.setRetainExistingValuesWhenParameterNotProvided(true, true);

return issueInputParameters;
}

private Optional<CustomField> getField(Issue issue) {
return customFieldManager.getCustomFieldObjects(issue)
.stream()
.filter(f -> f.getFieldName().equals(STORY_POINT_FILED_NAME))
.findFirst();
}

public Optional<String> getStoryPoints(String issueId) {
Issue issue = issueManager.getIssueObject(issueId);
Optional<Object> value = customFieldManager.getCustomFieldObjects(issue)
.stream()
.filter(f -> f.getFieldName().equals(STORY_POINT_FILED_NAME))
.map(f -> f.getValue(issue))
.filter(Objects::nonNull)
.findAny();
if (!value.isPresent()) {
return Optional.empty();
}

return format(value.get());
}

private Optional<String> format(Object value) {
if (value instanceof Double) {
DecimalFormat format = new DecimalFormat("0.#");
return Optional.of(format.format(value));
}

if (value instanceof Integer) {
return Optional.of(value.toString());
}

return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ public class PokerSession {
private final String issueId;
private final SessionStatus status;
private final long completionDate;
private final List<EstimationGrade> estimationGrades;
private final List<Estimate> estimates;
private final EstimationUnit estimationUnit;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ default Optional<Integer> getParam(HttpServletRequest request, String paramName)
}
}

//TODO: persistanceService should be injected to prcossors instead of passing as parameter
void process(PersistenceService persistenceService, HttpServletRequest request, String issueId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2021 Andriy Preizner
*
* This file is a part of Open Poker jira plugin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.aprey.jira.plugin.openpoker.api;

import com.aprey.jira.plugin.openpoker.persistence.PersistenceService;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

@Named
@Scanned
@Slf4j
public class ApplyVoteProcessor implements ActionProcessor {

private static final String ESTIMATE_FILED = "finalEstimateId";
private static final String USER_ID_FIELD = "userId";

@ComponentImport
private final UserManager userManager;

@Inject
public ApplyVoteProcessor(UserManager userManager) {
this.userManager = userManager;
}

@Override
public void process(PersistenceService persistenceService, HttpServletRequest request, String issueId) {
final long userId = Long.parseLong(request.getParameter(USER_ID_FIELD));
final int estimateId = Integer.parseInt(request.getParameter(ESTIMATE_FILED));
Optional<ApplicationUser> applicationUser = userManager.getUserById(userId);
if (!applicationUser.isPresent()) {
log.error("Application user is not found by {} id", userId);
return;
}

persistenceService.applyFinalEstimate(issueId, estimateId, applicationUser.get());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@
public class EstimateDTO {
private final UserDTO estimator;
private final String grade;
private final int gradeId;
}
Loading

0 comments on commit 4c77eb9

Please sign in to comment.