50 | * TODO: To be completely accurate, the changes should impose a dependency-tree ordering (via
51 | * --dependencies option) to GerritQuery! It is possible for an earlier ChangeId to be
52 | * refactored such that it is then dependent on a later change!
53 | */
54 | @Override
55 | @SuppressWarnings("deprecation")
56 | public int compareTo(GerritChange obj) {
57 | if (this != obj && obj != null) {
58 | int aNum = Integer.parseInt(this.getNumber());
59 | int bNum = Integer.parseInt(obj.getNumber());
60 |
61 | if (aNum == bNum) {
62 | return 0;
63 | } else {
64 | return aNum < bNum ? -1 : 1;
65 | }
66 | }
67 |
68 | return 0;
69 | }
70 |
71 | @Override
72 | public void fromJson(JSONObject json) {
73 | super.fromJson(json);
74 |
75 | this.lastUpdated = new Date(1000 * json.getLong(LAST_UPDATED));
76 |
77 | if (json.containsKey(GerritEventKeys.CURRENT_PATCH_SET)) {
78 | this.patchSet = new GerritPatchSet(json.getJSONObject(GerritEventKeys.CURRENT_PATCH_SET));
79 | }
80 |
81 | if (json.containsKey(GerritEventKeys.STATUS)) {
82 | this.setStatus(json.getString(GerritEventKeys.STATUS));
83 | }
84 |
85 | this.isOpen = json.getBoolean(GerritEventKeys.OPEN);
86 | }
87 |
88 | public Date getLastUpdated() {
89 | return lastUpdated;
90 | }
91 |
92 | public GerritPatchSet getPatchSet() {
93 | return patchSet;
94 | }
95 |
96 | public String getStatus() {
97 | return status;
98 | }
99 |
100 | public boolean isOpen() {
101 | return isOpen;
102 | }
103 |
104 | public void setLastUpdated(Date lastUpdated) {
105 | this.lastUpdated = lastUpdated;
106 | }
107 |
108 | public void setOpen(boolean isOpen) {
109 | this.isOpen = isOpen;
110 | }
111 |
112 | public void setPatchSet(GerritPatchSet patchSet) {
113 | this.patchSet = patchSet;
114 | }
115 |
116 | public void setStatus(String status) {
117 | this.status = status;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/data/dto/GerritPatchSet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.data.dto;
15 |
16 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.dto.GerritEventKeys;
17 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.dto.attr.PatchSet;
18 |
19 | import net.sf.json.JSONArray;
20 | import net.sf.json.JSONObject;
21 |
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | import java.util.ArrayList;
26 | import java.util.HashMap;
27 | import java.util.List;
28 | import java.util.Map;
29 |
30 | public class GerritPatchSet extends PatchSet {
31 | private static final Logger log = LoggerFactory.getLogger(GerritPatchSet.class);
32 |
33 | private List approvals;
34 |
35 | public GerritPatchSet() {
36 | super();
37 | }
38 |
39 | public GerritPatchSet(JSONObject json) {
40 | super(json);
41 | }
42 |
43 | @Override
44 | public void fromJson(JSONObject json) {
45 | log.debug("GerritPatchSet from json: " + json.toString(4, 0));
46 | super.fromJson(json);
47 |
48 | if (json.containsKey(GerritEventKeys.APPROVALS)) {
49 | JSONArray eventApprovals = json.getJSONArray(GerritEventKeys.APPROVALS);
50 | approvals = new ArrayList(eventApprovals.size());
51 |
52 | for (int i = 0; i < eventApprovals.size(); i++) {
53 | GerritApproval approval = new GerritApproval(eventApprovals.getJSONObject(i));
54 | approvals.add(approval);
55 | }
56 | } else {
57 | log.warn("GerritPatchSet contains no approvals key.");
58 | }
59 | }
60 |
61 | public List getApprovals() {
62 | return approvals;
63 | }
64 |
65 | public Map> getApprovalsByLabel() {
66 | Map> map = new HashMap<>();
67 | List l;
68 |
69 | for (GerritApproval approval : approvals) {
70 | String type = approval.getType();
71 |
72 | l = map.computeIfAbsent(type, k -> new ArrayList<>());
73 |
74 | l.add(approval);
75 | }
76 |
77 | return map;
78 | }
79 |
80 | public List getApprovalsForLabel(String label) {
81 | List filtered = new ArrayList<>();
82 |
83 | if (approvals != null) {
84 | for (GerritApproval approval : approvals) {
85 | if (approval.getType().equals(label)) {
86 | filtered.add(approval);
87 | }
88 | }
89 | }
90 |
91 | return filtered;
92 | }
93 |
94 | public void setApprovals(List approvals) {
95 | this.approvals = approvals;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/tabpanel/GerritEventKeys.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.tabpanel;
15 |
16 | /**
17 | * Extension of {@link com.sonyericsson.hudson.plugins.gerrit.gerritevents.dto.GerritEventKeys
18 | * sonyericsson.GerritEventKeys} to provide additional missing keys.
19 | *
20 | * @author Joe Hansche
21 | */
22 | public interface GerritEventKeys {
23 | public static final String APPROVALS = "approvals";
24 | public static final String BY = "by";
25 | public static final String CURRENT_PATCH_SET = "currentPatchSet";
26 | public static final String LAST_UPDATED = "lastUpdated";
27 | public static final String OPEN = "open";
28 | public static final String STATUS = "status";
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/tabpanel/GerritReviewIssueAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.tabpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.dto.GerritApproval;
17 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
18 |
19 | import com.atlassian.core.util.map.EasyMap;
20 | import com.atlassian.jira.datetime.DateTimeFormatter;
21 | import com.atlassian.jira.datetime.DateTimeStyle;
22 | import com.atlassian.jira.plugin.issuetabpanel.AbstractIssueAction;
23 | import com.atlassian.jira.plugin.issuetabpanel.IssueAction;
24 | import com.atlassian.jira.plugin.issuetabpanel.IssueTabPanelModuleDescriptor;
25 |
26 | import java.util.*;
27 |
28 | public class GerritReviewIssueAction extends AbstractIssueAction implements IssueAction {
29 | private String baseUrl;
30 | private GerritChange change;
31 | private DateTimeFormatter dateTimeFormatter;
32 |
33 | public GerritReviewIssueAction(IssueTabPanelModuleDescriptor descriptor, GerritChange change,
34 | DateTimeFormatter dateTimeFormatter, String baseUrl) {
35 | super(descriptor);
36 | this.dateTimeFormatter = dateTimeFormatter.forLoggedInUser();
37 | this.baseUrl = baseUrl;
38 | this.change = change;
39 | }
40 |
41 | @Override
42 | @SuppressWarnings("unchecked")
43 | protected void populateVelocityParams(@SuppressWarnings("rawtypes") Map params) {
44 | params.putAll(EasyMap.build("change", change,
45 | "formatLastUpdated", formatLastUpdated(),
46 | "isoLastUpdated", isoFormatLastUpdated(),
47 | "baseurl", this.baseUrl));
48 | }
49 |
50 | String formatLastUpdated() {
51 | return dateTimeFormatter.format(change.getLastUpdated());
52 | }
53 |
54 | String isoFormatLastUpdated() {
55 | return dateTimeFormatter.withStyle(DateTimeStyle.ISO_8601_DATE_TIME).format(change.getLastUpdated());
56 | }
57 |
58 | @Override
59 | public Date getTimePerformed() {
60 | return change.getLastUpdated();
61 | }
62 |
63 | @Override
64 | public boolean isDisplayActionAllTab() {
65 | return true;
66 | }
67 |
68 | /**
69 | * Returns the lowest score below 0 if available; otherwise the highest score above 0.
70 | *
71 | * @param approvals the approvals found on the Gerrit review
72 | * @return the approval that is deemed the "most significant"
73 | * @deprecated This functionality can now be found in the velocity template
74 | */
75 | @Deprecated
76 | GerritApproval getMostSignificantScore(final List approvals) {
77 | if (approvals != null) {
78 | try {
79 | GerritApproval min = Collections.min(approvals);
80 | GerritApproval max = Collections.max(approvals);
81 |
82 | if (min == max) {
83 | // Means there was only 1 vote, so show that one.
84 | return max;
85 | }
86 |
87 | if (min.getValueAsInt() < 0) {
88 | // There exists a negative vote, so show that one.
89 | return min;
90 | } else {
91 | // NOTE: Technically not possible to have a 0-score, but if one exists, use it!
92 | // No negative votes, so show the highest positive vote
93 | return max;
94 | }
95 | } catch (NoSuchElementException nsee) {
96 | // Collection was empty
97 | }
98 | }
99 |
100 | return null;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/tabpanel/GerritReviewsTabPanel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.tabpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.GerritConfiguration;
17 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
18 | import com.meetme.plugins.jira.gerrit.data.dto.GerritApproval;
19 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
20 | import com.meetme.plugins.jira.gerrit.data.dto.GerritPatchSet;
21 |
22 | import com.atlassian.jira.bc.user.search.UserSearchService;
23 | import com.atlassian.jira.datetime.DateTimeFormatter;
24 | import com.atlassian.jira.issue.Issue;
25 | import com.atlassian.jira.issue.tabpanels.GenericMessageAction;
26 | import com.atlassian.jira.plugin.issuetabpanel.*;
27 | import com.atlassian.jira.user.ApplicationUser;
28 | import com.atlassian.jira.user.UserUtils;
29 | import com.atlassian.jira.user.util.UserManager;
30 | import com.atlassian.sal.api.ApplicationProperties;
31 | import com.atlassian.sal.api.message.I18nResolver;
32 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
33 |
34 | import org.slf4j.Logger;
35 | import org.slf4j.LoggerFactory;
36 |
37 | import java.util.ArrayList;
38 | import java.util.Iterator;
39 | import java.util.List;
40 |
41 | /**
42 | * An {@link IssueTabPanel2 issue tab panel} for displaying all Gerrit code reviews related to this
43 | * issue.
44 | *
45 | * @author Joe Hansche
46 | */
47 | public class GerritReviewsTabPanel extends AbstractIssueTabPanel2 implements IssueTabPanel2 {
48 | private static final Logger log = LoggerFactory.getLogger(GerritReviewsTabPanel.class);
49 |
50 | private final DateTimeFormatter dateTimeFormatter;
51 | private final ApplicationProperties applicationProperties;
52 | private final GerritConfiguration configuration;
53 | private final IssueReviewsManager reviewsManager;
54 | private final I18nResolver i18n;
55 |
56 | @Deprecated
57 | private final UserManager userManager;
58 | private final UserSearchService userSearchService;
59 |
60 | public GerritReviewsTabPanel(
61 | @Deprecated UserManager userManager,
62 | UserSearchService userSearchService,
63 | DateTimeFormatter dateTimeFormatter,
64 | ApplicationProperties applicationProperties,
65 | GerritConfiguration configuration,
66 | IssueReviewsManager reviewsManager,
67 | I18nResolver i18n
68 | ) {
69 | this.userManager = userManager;
70 | this.userSearchService = userSearchService;
71 | this.dateTimeFormatter = dateTimeFormatter;
72 | this.applicationProperties = applicationProperties;
73 | this.configuration = configuration;
74 | this.reviewsManager = reviewsManager;
75 | this.i18n = i18n;
76 | }
77 |
78 | @Override
79 | public GetActionsReply getActions(GetActionsRequest request) {
80 | List issueActions;
81 |
82 | if (configuration.getSshHostname() == null || configuration.getSshUsername() == null || configuration.getSshPrivateKey() == null) {
83 | // Show not-configured error.
84 | issueActions = new ArrayList<>();
85 | issueActions.add(new GenericMessageAction("Configure Gerrit in Administration interface first."));
86 | } else {
87 | // List of items we will be showing in the tab panel.
88 | issueActions = getActions(request.issue());
89 | }
90 |
91 | return GetActionsReply.create(issueActions);
92 | }
93 |
94 | @Override
95 | public ShowPanelReply showPanel(ShowPanelRequest arg0) {
96 | boolean isShowing = true;
97 |
98 | if (!isConfigurationReady()) {
99 | isShowing = false;
100 | }
101 |
102 | return ShowPanelReply.create(isShowing);
103 | }
104 |
105 | /**
106 | * Get all {@link GerritReviewIssueAction}s related to the specified {@link Issue#getKey() issue
107 | * key}.
108 | *
109 | * @param issue the JIRA issue key
110 | * @return the set of {@link IssueAction}s for the issue
111 | */
112 | private List getActions(Issue issue) {
113 | log.debug("Getting actions for issue: {0}", issue.getKey());
114 |
115 | List issueActions = new ArrayList<>();
116 | List reviews;
117 |
118 | try {
119 | reviews = reviewsManager.getReviewsForIssue(issue);
120 | } catch (GerritQueryException exc) {
121 | exc.printStackTrace();
122 | issueActions.add(new GenericMessageAction(exc.getMessage()));
123 | return issueActions;
124 | }
125 |
126 | if (reviews.isEmpty()) {
127 | issueActions.add(new GenericMessageAction(i18n.getText("gerrit.tabpanel.no_changes")));
128 | } else {
129 | for (GerritChange change : reviews) {
130 | setUsersForChangeApprovals(change);
131 | issueActions.add(new GerritReviewIssueAction(descriptor(), change, dateTimeFormatter, applicationProperties.getBaseUrl()));
132 | // issueActions.add(new GenericMessageAction("
" + obj.toString(4) + "
"));
133 | }
134 | }
135 |
136 | return issueActions;
137 | }
138 |
139 | private ApplicationUser getUserByEmail(String email) {
140 | ApplicationUser user = null;
141 |
142 | if (email != null) {
143 | Iterator users = userSearchService.findUsersByEmail(email).iterator();
144 | if (users.hasNext()) user = users.next();
145 |
146 | if (user == null) user = UserUtils.getUserByEmail(email);
147 |
148 | if (user == null) {
149 | for (ApplicationUser iUser : userManager.getUsers()) {
150 | if (email.equalsIgnoreCase(iUser.getEmailAddress())) {
151 | user = iUser;
152 | break;
153 | }
154 | }
155 | }
156 | }
157 |
158 | return user;
159 | }
160 |
161 | private boolean isConfigurationReady() {
162 | return configuration.getSshHostname() != null && configuration.getSshUsername() != null
163 | && configuration.getSshPrivateKey() != null && configuration.getSshPrivateKey().exists();
164 | }
165 |
166 | private void setUsersForChangeApprovals(GerritChange change) {
167 | GerritPatchSet ps = change.getPatchSet();
168 |
169 | if (ps != null) {
170 | List approvals = ps.getApprovals();
171 |
172 | if (approvals != null) {
173 | for (GerritApproval approval : change.getPatchSet().getApprovals()) {
174 | String byEmail = approval.getByEmail();
175 | approval.setUser(getUserByEmail(byEmail));
176 | }
177 | }
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/tabpanel/SubtaskReviewsIssueAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.tabpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
17 | import com.meetme.plugins.jira.gerrit.workflow.condition.NoOpenReviews;
18 |
19 | import com.atlassian.jira.issue.Issue;
20 | import com.atlassian.jira.plugin.issuetabpanel.AbstractIssueAction;
21 | import com.atlassian.jira.plugin.issuetabpanel.IssueAction;
22 | import com.atlassian.jira.plugin.issuetabpanel.IssueTabPanelModuleDescriptor;
23 |
24 | import java.util.Date;
25 | import java.util.List;
26 | import java.util.Map;
27 |
28 | /**
29 | * @author Joe Hansche
30 | */
31 | public class SubtaskReviewsIssueAction extends AbstractIssueAction implements IssueAction {
32 |
33 | private Issue subtask;
34 | private List changes;
35 |
36 | public SubtaskReviewsIssueAction(IssueTabPanelModuleDescriptor descriptor, Issue subtask, List changes) {
37 | super(descriptor);
38 |
39 | this.subtask = subtask;
40 | this.changes = changes;
41 | }
42 |
43 | @Override
44 | public Date getTimePerformed() {
45 | return subtask.getUpdated();
46 | }
47 |
48 | @SuppressWarnings("unchecked")
49 | @Override
50 | protected void populateVelocityParams(@SuppressWarnings("rawtypes") Map velocityParams) {
51 | final int openReviews = changes == null ? 0 : NoOpenReviews.countReviewStatus(changes, true);
52 | final int closedReviews = changes == null ? 0 : NoOpenReviews.countReviewStatus(changes, false);
53 |
54 | // push data
55 | velocityParams.put("subtask", subtask);
56 | velocityParams.put("changes", changes);
57 | velocityParams.put("openReviews", openReviews);
58 | velocityParams.put("closedReviews", closedReviews);
59 | }
60 |
61 | @Override
62 | public boolean isDisplayActionAllTab() {
63 | return false;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/tabpanel/SubtaskReviewsTabPanel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.tabpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.GerritConfiguration;
17 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
18 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
19 |
20 | import com.atlassian.jira.issue.Issue;
21 | import com.atlassian.jira.plugin.issuetabpanel.*;
22 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Collection;
26 | import java.util.List;
27 |
28 | public class SubtaskReviewsTabPanel extends AbstractIssueTabPanel2 implements IssueTabPanel2 {
29 | private final GerritConfiguration configuration;
30 | private final IssueReviewsManager reviewsManager;
31 |
32 | public SubtaskReviewsTabPanel(GerritConfiguration configuration,
33 | IssueReviewsManager reviewsManager) {
34 | this.configuration = configuration;
35 | this.reviewsManager = reviewsManager;
36 | }
37 |
38 | @Override
39 | public GetActionsReply getActions(GetActionsRequest request) {
40 | Collection subtasks = request.issue().getSubTaskObjects();
41 | List actions = new ArrayList<>();
42 | List changes;
43 |
44 | for (Issue subtask : subtasks) {
45 | try {
46 | changes = getChanges(subtask);
47 | } catch (GerritQueryException e) {
48 | throw new RuntimeException(e);
49 | }
50 |
51 | actions.add(new SubtaskReviewsIssueAction(descriptor(), subtask, changes));
52 | }
53 |
54 | return GetActionsReply.create(actions);
55 | }
56 |
57 | @Override
58 | public ShowPanelReply showPanel(ShowPanelRequest request) {
59 | boolean show = false;
60 |
61 | if (isConfigurationReady()) {
62 | Collection subtasks = request.issue().getSubTaskObjects();
63 | show = subtasks != null && subtasks.size() > 0;
64 | }
65 |
66 | return ShowPanelReply.create(show);
67 | }
68 |
69 | private List getChanges(Issue subtask) throws GerritQueryException {
70 | return reviewsManager.getReviewsForIssue(subtask);
71 | }
72 |
73 | private boolean isConfigurationReady() {
74 | final GerritConfiguration configuration = this.configuration;
75 |
76 | return configuration != null && configuration.getSshHostname() != null && configuration.getSshUsername() != null
77 | && configuration.getSshPrivateKey() != null && configuration.getSshPrivateKey().exists();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/webpanel/GerritReviewsIssueAgilePanel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.webpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
17 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
18 |
19 | import com.atlassian.jira.issue.Issue;
20 | import com.atlassian.jira.plugin.webfragment.contextproviders.AbstractJiraContextProvider;
21 | import com.atlassian.jira.plugin.webfragment.model.JiraHelper;
22 | import com.atlassian.jira.user.ApplicationUser;
23 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
24 |
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 |
29 | @SuppressWarnings("unchecked")
30 | public class GerritReviewsIssueAgilePanel extends AbstractJiraContextProvider {
31 | private static final String KEY_ISSUE = "issue";
32 | private static final String KEY_CHANGES = "changes";
33 | private static final String KEY_ERROR = "error";
34 |
35 | private IssueReviewsManager reviewsManager;
36 |
37 | public GerritReviewsIssueAgilePanel(IssueReviewsManager reviewsManager) {
38 | super();
39 | this.reviewsManager = reviewsManager;
40 | }
41 |
42 | @Override
43 | public Map getContextMap(ApplicationUser user, JiraHelper jiraHelper) {
44 | HashMap contextMap = new HashMap<>();
45 |
46 | Issue currentIssue = (Issue) jiraHelper.getContextParams().get(KEY_ISSUE);
47 |
48 | try {
49 | List changes = reviewsManager.getReviewsForIssue(currentIssue);
50 | contextMap.put(KEY_CHANGES, changes);
51 | contextMap.put("atl.gh.issue.details.tab.count", (long) changes.size());
52 | } catch (GerritQueryException e) {
53 | contextMap.put(KEY_ERROR, e.getMessage());
54 | }
55 |
56 | return contextMap;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/webpanel/GerritReviewsIssueSidePanel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.webpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
17 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
18 |
19 | import com.atlassian.jira.issue.Issue;
20 | import com.atlassian.jira.plugin.webfragment.contextproviders.AbstractJiraContextProvider;
21 | import com.atlassian.jira.plugin.webfragment.model.JiraHelper;
22 | import com.atlassian.jira.user.ApplicationUser;
23 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
24 |
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 |
29 | @SuppressWarnings("unchecked")
30 | public class GerritReviewsIssueSidePanel extends AbstractJiraContextProvider {
31 | private static final String KEY_ISSUE = "issue";
32 | private static final String KEY_CHANGES = "changes";
33 | private static final String KEY_ERROR = "error";
34 |
35 | private IssueReviewsManager reviewsManager;
36 |
37 | public GerritReviewsIssueSidePanel(IssueReviewsManager reviewsManager) {
38 | super();
39 | this.reviewsManager = reviewsManager;
40 | }
41 |
42 | @Override
43 | public Map getContextMap(ApplicationUser user, JiraHelper jiraHelper) {
44 | HashMap contextMap = new HashMap<>();
45 |
46 | Issue currentIssue = (Issue) jiraHelper.getContextParams().get(KEY_ISSUE);
47 |
48 | try {
49 | List changes = reviewsManager.getReviewsForIssue(currentIssue);
50 | contextMap.put(KEY_CHANGES, changes);
51 | } catch (GerritQueryException e) {
52 | contextMap.put(KEY_ERROR, e.getMessage());
53 | }
54 |
55 | return contextMap;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/webpanel/IssueStatusOptionsProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.webpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.SessionKeys;
17 |
18 | import com.atlassian.jira.issue.Issue;
19 | import com.atlassian.jira.security.JiraAuthenticationContext;
20 | import com.atlassian.jira.util.I18nHelper;
21 | import com.atlassian.jira.util.collect.CollectionBuilder;
22 | import com.atlassian.jira.util.velocity.VelocityRequestContext;
23 | import com.atlassian.jira.util.velocity.VelocityRequestContextFactory;
24 | import com.atlassian.jira.util.velocity.VelocityRequestSession;
25 | import com.atlassian.plugin.web.api.WebItem;
26 | import com.atlassian.plugin.web.api.model.WebFragmentBuilder;
27 | import com.atlassian.plugin.web.api.provider.WebItemProvider;
28 |
29 | import org.slf4j.Logger;
30 | import org.slf4j.LoggerFactory;
31 |
32 | import java.util.Collections;
33 | import java.util.Map;
34 |
35 | public class IssueStatusOptionsProvider implements WebItemProvider {
36 | private static final Logger log = LoggerFactory.getLogger(IssueStatusOptionsProvider.class);
37 |
38 | private static final String STATUS_OPEN = "Open";
39 | private static final String STATUS_ALL = "All";
40 | static final String DEFAULT_STATUS = STATUS_OPEN;
41 |
42 | private VelocityRequestContextFactory requestContextFactory;
43 | private JiraAuthenticationContext authenticationContext;
44 |
45 | public IssueStatusOptionsProvider(VelocityRequestContextFactory requestContextFactory, JiraAuthenticationContext authenticationContext) {
46 | this.requestContextFactory = requestContextFactory;
47 | this.authenticationContext = authenticationContext;
48 | }
49 |
50 | @Override
51 | public Iterable getItems(Map params) {
52 | final VelocityRequestContext requestContext = requestContextFactory.getJiraVelocityRequestContext();
53 | final I18nHelper i18n = authenticationContext.getI18nHelper();
54 | final Issue issue = (Issue) params.get("issue");
55 |
56 | final VelocityRequestSession session = requestContext.getSession();
57 | final String baseUrl = requestContext.getBaseUrl();
58 |
59 | String issueStatus = (String) session.getAttribute(SessionKeys.VIEWISSUE_REVIEWS_ISSUESTATUS);
60 |
61 | if (issueStatus == null) {
62 | issueStatus = DEFAULT_STATUS;
63 | }
64 |
65 | if (issue.getSubTaskObjects().isEmpty() && isIssueOpen(issue)) {
66 | return Collections.emptyList();
67 | }
68 |
69 | int weight = 10;
70 |
71 | final WebItem allLink = new WebFragmentBuilder(weight += 10)
72 | .id("reviews-issuestatus-all")
73 | .label(i18n.getText("gerrit-reviews-left-panel.options.issuestatus.all"))
74 | .styleClass(getStyleFor(issueStatus, STATUS_ALL))
75 | .webItem("issuestatus-view-options")
76 | .url(getUrlForType(STATUS_ALL, baseUrl, issue))
77 | .build();
78 |
79 | WebItem openLink = new WebFragmentBuilder(weight += 10)
80 | .id("reviews-issuestatus-open")
81 | .label(i18n.getText("gerrit-reviews-left-panel.options.issuestatus.open"))
82 | .styleClass(getStyleFor(issueStatus, STATUS_OPEN))
83 | .webItem("issuestatus-view-options")
84 | .url(getUrlForType(STATUS_OPEN, baseUrl, issue))
85 | .build();
86 |
87 | return CollectionBuilder.list(allLink, openLink);
88 | }
89 |
90 | private String getUrlForType(String type, String baseUrl, Issue issue) {
91 | return baseUrl + "/browse/" + issue.getKey() + "?gerritIssueStatus=" + type + "#gerrit-reviews-left-panel";
92 | }
93 |
94 | private String getStyleFor(String type, String expecting) {
95 | return expecting.equals(type) ? "aui-list-checked aui-checked" : "aui-list-checked";
96 | }
97 |
98 | static boolean isIssueOpen(Issue issue) {
99 | log.debug("Checking if " + issue.getKey() + " is open: " + issue.getResolutionObject());
100 | return issue.getResolutionObject() == null;
101 | }
102 |
103 | static boolean wantsUnresolved(final String gerritIssueStatus) {
104 | return STATUS_ALL.equals(gerritIssueStatus);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/webpanel/IssueTypeOptionsProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.webpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.SessionKeys;
17 |
18 | import com.atlassian.jira.issue.Issue;
19 | import com.atlassian.jira.security.JiraAuthenticationContext;
20 | import com.atlassian.jira.util.I18nHelper;
21 | import com.atlassian.jira.util.collect.CollectionBuilder;
22 | import com.atlassian.jira.util.velocity.VelocityRequestContext;
23 | import com.atlassian.jira.util.velocity.VelocityRequestContextFactory;
24 | import com.atlassian.jira.util.velocity.VelocityRequestSession;
25 | import com.atlassian.plugin.web.api.WebItem;
26 | import com.atlassian.plugin.web.api.model.WebFragmentBuilder;
27 | import com.atlassian.plugin.web.api.provider.WebItemProvider;
28 |
29 | import org.apache.commons.lang.StringUtils;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | import java.util.Map;
34 |
35 | public class IssueTypeOptionsProvider implements WebItemProvider {
36 | private static final Logger log = LoggerFactory.getLogger(IssueTypeOptionsProvider.class);
37 |
38 | public static final String ISSUE_ONLY = "IssueOnly";
39 | public static final String SUBTASK_ONLY = "SubtaskOnly";
40 | public static final String ALL_ISSUES = "All";
41 |
42 | public static final String DEFAULT_ISSUE_TYPE = ISSUE_ONLY;
43 |
44 | private VelocityRequestContextFactory requestContextFactory;
45 | private JiraAuthenticationContext authenticationContext;
46 |
47 | public IssueTypeOptionsProvider(VelocityRequestContextFactory requestContextFactory, JiraAuthenticationContext authenticationContext) {
48 | this.requestContextFactory = requestContextFactory;
49 | this.authenticationContext = authenticationContext;
50 | }
51 |
52 | @Override
53 | public Iterable getItems(Map params) {
54 | final VelocityRequestContext requestContext = requestContextFactory.getJiraVelocityRequestContext();
55 | final I18nHelper i18n = authenticationContext.getI18nHelper();
56 | final Issue issue = (Issue) params.get("issue");
57 |
58 | final VelocityRequestSession session = requestContext.getSession();
59 | final String baseUrl = requestContext.getBaseUrl();
60 |
61 | String issueType = (String) session.getAttribute(SessionKeys.VIEWISSUE_REVIEWS_ISSUETYPE);
62 |
63 | if (StringUtils.isEmpty(issueType) || issue.getSubTaskObjects().isEmpty()) {
64 | issueType = DEFAULT_ISSUE_TYPE;
65 | }
66 |
67 | int weight = 10;
68 | WebItem issueOnlyLink = new WebFragmentBuilder(weight += 10)
69 | .id("reviews-issuetype-issueonly")
70 | .label(i18n.getText("gerrit-reviews-left-panel.options.issuetype.issue_only"))
71 | .styleClass(getStyleFor(issueType, ISSUE_ONLY))
72 | .webItem("issuetype-view-options")
73 | .url(getUrlForType(ISSUE_ONLY, baseUrl, issue))
74 | .build();
75 |
76 | if (issue.getSubTaskObjects().isEmpty()) {
77 | // Contains no subtasks, so no reason to show the others
78 | return CollectionBuilder.list(issueOnlyLink);
79 | }
80 |
81 | // Contains subtasks, expose the other options now
82 | final WebItem subtaskOnlyLink = new WebFragmentBuilder(weight += 10)
83 | .id("reviews-issuetype-subtasksonly")
84 | .label(i18n.getText("gerrit-reviews-left-panel.options.issuetype.subtasks_only"))
85 | .styleClass(getStyleFor(issueType, SUBTASK_ONLY))
86 | .webItem("issuetype-view-options")
87 | .url(getUrlForType(SUBTASK_ONLY, baseUrl, issue))
88 | .build();
89 |
90 | final WebItem allLink = new WebFragmentBuilder(weight += 10)
91 | .id("reviews-issuetype-all")
92 | .label(i18n.getText("gerrit-reviews-left-panel.options.issuetype.all"))
93 | .styleClass(getStyleFor(issueType, ALL_ISSUES))
94 | .webItem("issuetype-view-options")
95 | .url(getUrlForType(ALL_ISSUES, baseUrl, issue))
96 | .build();
97 |
98 | return CollectionBuilder.list(issueOnlyLink, subtaskOnlyLink, allLink);
99 | }
100 |
101 | private String getUrlForType(String type, String baseUrl, Issue issue) {
102 | return baseUrl + "/browse/" + issue.getKey() + "?gerritIssueType=" + type
103 | + "#gerrit-reviews-left-panel";
104 | }
105 |
106 | private String getStyleFor(String type, String expecting) {
107 | return expecting.equals(type) ? "aui-list-checked aui-checked" : "aui-list-checked";
108 | }
109 |
110 | public static final boolean wantsSubtasks(final String gerritIssueType) {
111 | return SUBTASK_ONLY.equals(gerritIssueType) || ALL_ISSUES.equals(gerritIssueType);
112 | }
113 |
114 | public static final boolean wantsIssue(final String gerritIssueType) {
115 | return ISSUE_ONLY.equals(gerritIssueType) || ALL_ISSUES.equals(gerritIssueType);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/webpanel/ReviewStatusOptionsProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.webpanel;
15 |
16 | import com.meetme.plugins.jira.gerrit.SessionKeys;
17 |
18 | import com.atlassian.jira.issue.Issue;
19 | import com.atlassian.jira.security.JiraAuthenticationContext;
20 | import com.atlassian.jira.util.I18nHelper;
21 | import com.atlassian.jira.util.collect.CollectionBuilder;
22 | import com.atlassian.jira.util.velocity.VelocityRequestContext;
23 | import com.atlassian.jira.util.velocity.VelocityRequestContextFactory;
24 | import com.atlassian.jira.util.velocity.VelocityRequestSession;
25 | import com.atlassian.plugin.web.api.WebItem;
26 | import com.atlassian.plugin.web.api.model.WebFragmentBuilder;
27 | import com.atlassian.plugin.web.api.provider.WebItemProvider;
28 |
29 | import java.util.Map;
30 |
31 | public class ReviewStatusOptionsProvider implements WebItemProvider {
32 | public static final String STATUS_OPEN = "Open";
33 | public static final String STATUS_ALL = "All";
34 | public static final String DEFAULT_STATUS = STATUS_ALL;
35 |
36 | private VelocityRequestContextFactory requestContextFactory;
37 | private JiraAuthenticationContext authenticationContext;
38 |
39 | public ReviewStatusOptionsProvider(VelocityRequestContextFactory requestContextFactory, JiraAuthenticationContext authenticationContext) {
40 | this.requestContextFactory = requestContextFactory;
41 | this.authenticationContext = authenticationContext;
42 | }
43 |
44 | @Override
45 | public Iterable getItems(Map params) {
46 | final VelocityRequestContext requestContext = requestContextFactory.getJiraVelocityRequestContext();
47 | final I18nHelper i18n = authenticationContext.getI18nHelper();
48 | final Issue issue = (Issue) params.get("issue");
49 |
50 | final VelocityRequestSession session = requestContext.getSession();
51 | final String baseUrl = requestContext.getBaseUrl();
52 |
53 | String reviewStatus = (String) session.getAttribute(SessionKeys.VIEWISSUE_REVIEWS_REVIEWSTATUS);
54 |
55 | if (reviewStatus == null) {
56 | reviewStatus = DEFAULT_STATUS;
57 | }
58 |
59 | int weight = 10;
60 |
61 | final WebItem allLink = new WebFragmentBuilder(weight += 10)
62 | .id("reviews-reviewstatus-all")
63 | .label(i18n.getText("gerrit-reviews-left-panel.options.reviewstatus.all"))
64 | .styleClass(getStyleFor(reviewStatus, STATUS_ALL))
65 | .webItem("reviewstatus-view-options")
66 | .url(getUrlForType(STATUS_ALL, baseUrl, issue))
67 | .build();
68 |
69 | final WebItem openLink = new WebFragmentBuilder(weight += 10)
70 | .id("reviews-reviewstatus-open")
71 | .label(i18n.getText("gerrit-reviews-left-panel.options.reviewstatus.open"))
72 | .styleClass(getStyleFor(reviewStatus, STATUS_OPEN))
73 | .webItem("reviewstatus-view-options")
74 | .url(getUrlForType(STATUS_OPEN, baseUrl, issue))
75 | .build();
76 |
77 | return CollectionBuilder.list(allLink, openLink);
78 | }
79 |
80 | private String getUrlForType(String type, String baseUrl, Issue issue) {
81 | return baseUrl + "/browse/" + issue.getKey() + "?gerritReviewStatus=" + type + "#gerrit-reviews-left-panel";
82 | }
83 |
84 | private String getStyleFor(String type, String expecting) {
85 | return expecting.equals(type) ? "aui-list-checked aui-checked" : "aui-list-checked";
86 | }
87 |
88 | public static boolean wantsClosedReviews(String gerritReviewStatus) {
89 | return STATUS_ALL.equals(gerritReviewStatus);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/webpanel/ShowReviewsWebPanelCondition.java:
--------------------------------------------------------------------------------
1 | package com.meetme.plugins.jira.gerrit.webpanel;
2 |
3 | import com.meetme.plugins.jira.gerrit.data.GerritConfiguration;
4 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
5 |
6 | import com.atlassian.jira.issue.Issue;
7 | import com.atlassian.plugin.web.Condition;
8 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
9 |
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import java.util.Map;
14 |
15 | import static org.apache.commons.collections.CollectionUtils.isEmpty;
16 |
17 | /**
18 | * Created by jhansche on 9/2/16.
19 | */
20 | public class ShowReviewsWebPanelCondition implements Condition {
21 |
22 | private static final Logger log = LoggerFactory.getLogger(ShowReviewsWebPanelCondition.class);
23 |
24 | private static final String KEY_ISSUE = "issue";
25 |
26 | private final GerritConfiguration gerritConfiguration;
27 | private final IssueReviewsManager issueReviewsManager;
28 |
29 | public ShowReviewsWebPanelCondition(IssueReviewsManager reviewsManager, GerritConfiguration configurationManager) {
30 |
31 | issueReviewsManager = reviewsManager;
32 | gerritConfiguration = configurationManager;
33 | }
34 |
35 | @Override
36 | public void init(Map map) {
37 | }
38 |
39 | @Override
40 | public boolean shouldDisplay(Map map) {
41 |
42 | if (map == null) {
43 | return false;
44 | }
45 |
46 | final Issue issue = (Issue) map.get(KEY_ISSUE);
47 |
48 | if (issue == null) {
49 | return false;
50 | }
51 |
52 | // Shall the system use the white list and does the issue belongs to a project, that uses gerrit:
53 | if (gerritConfiguration.getUseGerritProjectWhitelist() && !isGerritProject(issue)) {
54 | return false;
55 | }
56 |
57 | // Even though there are no reviews, the gerrit panel shall be displayed:
58 | if (gerritConfiguration.getShowsEmptyPanel()) {
59 | return true;
60 | }
61 |
62 | try {
63 |
64 | return !isEmpty(issueReviewsManager.getReviewsForIssue(issue));
65 | } catch (GerritQueryException gerritQueryException) {
66 |
67 | log.warn(gerritQueryException.getLocalizedMessage(), gerritQueryException);
68 | return false;
69 | }
70 | }
71 |
72 | private boolean isGerritProject(final Issue issue) {
73 |
74 | return issue.getProjectId() != null
75 | && !isEmpty(gerritConfiguration.getIdsOfKnownGerritProjects())
76 | && gerritConfiguration.getIdsOfKnownGerritProjects().contains(issue.getProjectId().toString());
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/workflow/ApprovalScoreConditionFactoryImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.workflow;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.dto.GerritApproval;
17 | import com.meetme.plugins.jira.gerrit.workflow.condition.ApprovalScore;
18 |
19 | import com.atlassian.core.util.map.EasyMap;
20 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
21 | import com.atlassian.jira.plugin.workflow.WorkflowPluginConditionFactory;
22 | import com.opensymphony.workflow.loader.AbstractDescriptor;
23 | import com.opensymphony.workflow.loader.ConditionDescriptor;
24 |
25 | import java.util.Arrays;
26 | import java.util.Collections;
27 | import java.util.List;
28 | import java.util.Map;
29 |
30 | public class ApprovalScoreConditionFactoryImpl extends AbstractWorkflowPluginFactory implements WorkflowPluginConditionFactory {
31 | private static final List ALL_PARAMS = Collections.unmodifiableList(Arrays.asList(ApprovalScore.KEY_NEGATIVE,
32 | ApprovalScore.KEY_COMPARISON, ApprovalScore.KEY_TARGET, ApprovalScore.KEY_LABEL));
33 |
34 | private static final boolean DEFAULT_NEGATIVE = false;
35 | private static final ApprovalScore.ComparisonOperator DEFAULT_COMPARISON = ApprovalScore.ComparisonOperator.EQUAL_TO;
36 | private static final int DEFAULT_TARGET = 0;
37 | private static final String DEFAULT_LABEL = "Code-Review";
38 |
39 | @SuppressWarnings("unchecked")
40 | @Override
41 | public Map getDescriptorParams(Map conditionParams) {
42 | if (conditionParams != null && conditionParams.containsKey(ApprovalScore.KEY_NEGATIVE)
43 | && conditionParams.containsKey(ApprovalScore.KEY_COMPARISON) && conditionParams.containsKey(ApprovalScore.KEY_TARGET)
44 | && conditionParams.containsKey(ApprovalScore.KEY_LABEL)) {
45 | return extractMultipleParams(conditionParams, ALL_PARAMS);
46 | }
47 |
48 | return EasyMap.build();
49 | }
50 |
51 | @Override
52 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
53 | velocityParams.put(ApprovalScore.KEY_NEGATIVE, isReversed(descriptor));
54 | velocityParams.put(ApprovalScore.KEY_COMPARISON, getComparison(descriptor));
55 | velocityParams.put(ApprovalScore.KEY_TARGET, getTarget(descriptor));
56 | velocityParams.put(ApprovalScore.KEY_LABEL, getLabel(descriptor));
57 | }
58 |
59 | @Override
60 | protected void getVelocityParamsForInput(Map velocityParams) {
61 | velocityParams.put(ApprovalScore.KEY_NEGATIVE, DEFAULT_NEGATIVE);
62 | velocityParams.put(ApprovalScore.KEY_COMPARISON, DEFAULT_COMPARISON);
63 | velocityParams.put(ApprovalScore.KEY_TARGET, DEFAULT_TARGET);
64 | velocityParams.put(ApprovalScore.KEY_LABEL, DEFAULT_LABEL);
65 | }
66 |
67 | @Override
68 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
69 | velocityParams.put(ApprovalScore.KEY_NEGATIVE, isReversed(descriptor));
70 | velocityParams.put(ApprovalScore.KEY_COMPARISON, getComparison(descriptor));
71 | velocityParams.put(ApprovalScore.KEY_TARGET, getTarget(descriptor));
72 | velocityParams.put(ApprovalScore.KEY_LABEL, getLabel(descriptor));
73 | }
74 |
75 | private String getStringFromDescriptor(AbstractDescriptor descriptor, String key) {
76 | if (!(descriptor instanceof ConditionDescriptor)) {
77 | throw new IllegalArgumentException("Descriptor must be a ConditionDescriptor.");
78 | }
79 |
80 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
81 |
82 | return (String) conditionDescriptor.getArgs().get(key);
83 | }
84 |
85 | private boolean isReversed(AbstractDescriptor descriptor) {
86 | String value = getStringFromDescriptor(descriptor, ApprovalScore.KEY_NEGATIVE);
87 | return Boolean.parseBoolean(value);
88 | }
89 |
90 | private String getLabel(AbstractDescriptor descriptor) {
91 | return GerritApproval.getUpgradedLabelType(getStringFromDescriptor(descriptor, ApprovalScore.KEY_LABEL));
92 | }
93 |
94 | private int getTarget(AbstractDescriptor descriptor) {
95 | String value = getStringFromDescriptor(descriptor, ApprovalScore.KEY_TARGET);
96 | return Integer.parseInt(value);
97 | }
98 |
99 | private ApprovalScore.ComparisonOperator getComparison(AbstractDescriptor descriptor) {
100 | String value = getStringFromDescriptor(descriptor, ApprovalScore.KEY_COMPARISON);
101 | return ApprovalScore.ComparisonOperator.valueOf(value);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/workflow/ApproveReviewFactoryImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.workflow;
15 |
16 | import com.meetme.plugins.jira.gerrit.workflow.function.ApprovalFunction;
17 |
18 | import com.atlassian.core.util.map.EasyMap;
19 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
20 | import com.atlassian.jira.plugin.workflow.WorkflowPluginFunctionFactory;
21 | import com.opensymphony.workflow.loader.AbstractDescriptor;
22 | import com.opensymphony.workflow.loader.FunctionDescriptor;
23 |
24 | import java.util.Map;
25 |
26 | public class ApproveReviewFactoryImpl extends AbstractWorkflowPluginFactory implements
27 | WorkflowPluginFunctionFactory {
28 |
29 | // 1. Review type and score (e.g., "--verified 1") -- one input or two?
30 | // 2. Message (e.g., --message "Ready for Test")
31 | // 3. Submit (--submit)
32 |
33 | // OR: [args] and let the admin fill it out?
34 |
35 | @SuppressWarnings("unchecked")
36 | @Override
37 | public Map getDescriptorParams(Map params) {
38 | if (params != null && params.containsKey(ApprovalFunction.KEY_CMD_ARGS)) {
39 | return EasyMap.build(ApprovalFunction.KEY_CMD_ARGS, extractSingleParam(params, ApprovalFunction.KEY_CMD_ARGS));
40 | }
41 |
42 | // Create a 'hard coded' parameter
43 | return EasyMap.build(ApprovalFunction.KEY_CMD_ARGS, ApprovalFunction.DEFAULT_CMD_ARGS);
44 | }
45 |
46 | @Override
47 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
48 | velocityParams.put(ApprovalFunction.KEY_CMD_ARGS, getCommandArgs(descriptor));
49 | }
50 |
51 | @Override
52 | protected void getVelocityParamsForInput(Map velocityParams) {
53 | velocityParams.put(ApprovalFunction.KEY_CMD_ARGS, ApprovalFunction.DEFAULT_CMD_ARGS);
54 | }
55 |
56 | @Override
57 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
58 | velocityParams.put(ApprovalFunction.KEY_CMD_ARGS, getCommandArgs(descriptor));
59 | }
60 |
61 | private Object getCommandArgs(AbstractDescriptor descriptor) {
62 | if (!(descriptor instanceof FunctionDescriptor)) {
63 | throw new IllegalArgumentException("Descriptor must be a FunctionDescriptor.");
64 | }
65 |
66 | FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
67 | String args = (String) functionDescriptor.getArgs().get(ApprovalFunction.KEY_CMD_ARGS);
68 |
69 | if (args != null && args.trim().length() > 0) {
70 | return args;
71 | } else {
72 | return ApprovalFunction.DEFAULT_CMD_ARGS;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/workflow/NoOpenReviewsConditionFactoryImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.workflow;
15 |
16 | import com.meetme.plugins.jira.gerrit.workflow.condition.NoOpenReviews;
17 |
18 | import com.atlassian.core.util.map.EasyMap;
19 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
20 | import com.atlassian.jira.plugin.workflow.WorkflowPluginConditionFactory;
21 | import com.opensymphony.workflow.loader.AbstractDescriptor;
22 | import com.opensymphony.workflow.loader.ConditionDescriptor;
23 |
24 | import java.util.Map;
25 |
26 | public class NoOpenReviewsConditionFactoryImpl extends AbstractWorkflowPluginFactory implements WorkflowPluginConditionFactory {
27 |
28 | @SuppressWarnings("unchecked")
29 | @Override
30 | public Map getDescriptorParams(Map conditionParams) {
31 | if (conditionParams != null && conditionParams.containsKey(NoOpenReviews.KEY_REVERSED)) {
32 | return EasyMap.build(NoOpenReviews.KEY_REVERSED, extractSingleParam(conditionParams, NoOpenReviews.KEY_REVERSED));
33 | }
34 |
35 | return EasyMap.build();
36 | }
37 |
38 | @Override
39 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
40 | velocityParams.put(NoOpenReviews.KEY_REVERSED, isReversed(descriptor));
41 | }
42 |
43 | @Override
44 | protected void getVelocityParamsForInput(Map velocityParams) {
45 | // Nothing to choose from, because boolean is only ON/OFF
46 | }
47 |
48 | @Override
49 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
50 | velocityParams.put(NoOpenReviews.KEY_REVERSED, isReversed(descriptor));
51 | }
52 |
53 | private boolean isReversed(AbstractDescriptor descriptor) {
54 | if (!(descriptor instanceof ConditionDescriptor)) {
55 | throw new IllegalArgumentException("Descriptor must be a ConditionDescriptor.");
56 | }
57 |
58 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
59 |
60 | String value = (String) conditionDescriptor.getArgs().get(NoOpenReviews.KEY_REVERSED);
61 | return Boolean.parseBoolean(value);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/workflow/condition/ApprovalScore.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.workflow.condition;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
17 | import com.meetme.plugins.jira.gerrit.data.dto.GerritApproval;
18 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
19 |
20 | import com.atlassian.jira.issue.Issue;
21 | import com.atlassian.jira.workflow.condition.AbstractJiraCondition;
22 | import com.opensymphony.module.propertyset.PropertySet;
23 | import com.opensymphony.workflow.WorkflowException;
24 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
25 |
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import java.util.List;
30 | import java.util.Map;
31 |
32 | /**
33 | * A workflow condition that requires (or rejects) a certain Gerrit approval score.
34 | *
35 | * An example use case might be to require, for example, "{@literal MUST have a Code-Review score >= 2}", or
36 | * "{@literal Must NOT have a Code-Review score < 0}" (these could even be combined into a single transition to
37 | * require both conditions be met).
38 | *
39 | * @author Joe Hansche
40 | */
41 | public class ApprovalScore extends AbstractJiraCondition {
42 | private static final Logger log = LoggerFactory.getLogger(ApprovalScore.class);
43 |
44 | // Must [or not] have [ operator ] N score for [type]
45 | public static final String KEY_NEGATIVE = "negative";
46 | public static final String KEY_COMPARISON = "comparison";
47 | public static final String KEY_TARGET = "target";
48 | public static final String KEY_LABEL = "label";
49 |
50 | private IssueReviewsManager reviewsManager;
51 |
52 | public ApprovalScore(final IssueReviewsManager reviewsManager) {
53 | this.reviewsManager = reviewsManager;
54 | }
55 |
56 | @Override
57 | public boolean passesCondition(@SuppressWarnings("rawtypes") Map transientVars, @SuppressWarnings("rawtypes") Map args, PropertySet ps)
58 | throws WorkflowException {
59 | Issue issue = getIssue(transientVars);
60 | List reviews;
61 |
62 | try {
63 | reviews = reviewsManager.getReviewsForIssue(issue);
64 | } catch (GerritQueryException e) {
65 | // If there's an error, best not to block the workflow, and just act like it passes??
66 | throw new WorkflowException(e);
67 | }
68 |
69 | boolean isReverse = Boolean.parseBoolean((String) args.get(KEY_NEGATIVE));
70 | ComparisonOperator op = ComparisonOperator.valueOf((String) args.get(KEY_COMPARISON));
71 | String label = (String) args.get(KEY_LABEL);
72 | int targetScore = Integer.parseInt((String) args.get(KEY_TARGET));
73 |
74 | String description = describe(isReverse, label, op, targetScore);
75 | log.debug("Condition description: " + description);
76 |
77 | boolean matches = false;
78 | int matchingChanges = 0;
79 | int blockingChanges = 0;
80 |
81 | for (GerritChange ch : reviews) {
82 | int matchingApprovals = 0;
83 |
84 | for (GerritApproval approval : ch.getPatchSet().getApprovals()) {
85 | if (approval.getType().equals(label)) {
86 | if (compareScore(op, approval.getValueAsInt(), targetScore)) {
87 | log.debug("Found a match on review " + ch + " for condition: " + description + "; Approver=" + approval);
88 |
89 | matchingApprovals++;
90 | }
91 | }
92 | }
93 |
94 | if (matchingApprovals > 0) {
95 | matchingChanges++;
96 | } else {
97 | blockingChanges++;
98 | }
99 | }
100 |
101 | // To be considered a match, every change must have at least one matching approval, and no
102 | // change can be missing a matching approval
103 | matches = matchingChanges > 0 && blockingChanges == 0;
104 |
105 | if (isReverse) {
106 | matches = !matches;
107 | log.debug("Negating logic, due to 'MUST NOT' condition. NEW matches=" + matches);
108 | }
109 |
110 | log.trace("Evaluating conditions: " + matches);
111 |
112 | return matches;
113 | }
114 |
115 | private String describe(boolean isReverse, String label, ComparisonOperator op, int targetScore) {
116 | return "isReverse=" + isReverse + ", label=" + label + ", op=" + op.name() + ", targetScore=" + targetScore;
117 | }
118 |
119 | /**
120 | * Compare two scores using the provided {@link ComparisonOperator}
121 | *
122 | * @param oper the comparison operator
123 | * @param score the score that is being compared
124 | * @param target the target score against which {@code score} is being compared
125 | * @return the result of the comparison
126 | */
127 | private boolean compareScore(ComparisonOperator oper, int score, int target) {
128 | log.debug("Comparing score: " + score + oper + target);
129 |
130 | switch (oper) {
131 | case EQUAL_TO:
132 | return score == target;
133 | case LESS_THAN:
134 | return score < target;
135 | case LESS_OR_EQUAL:
136 | return score <= target;
137 | case GREATER_OR_EQUAL:
138 | return score >= target;
139 | case GREATER_THAN:
140 | return score > target;
141 | }
142 |
143 | throw new IllegalArgumentException("Unknown operator: " + oper);
144 | }
145 |
146 | /**
147 | * Text-based selection of comparison operators.
148 | *
149 | * @author Joe Hansche
150 | */
151 | public static enum ComparisonOperator {
152 | LESS_THAN("<"), LESS_OR_EQUAL("<="), EQUAL_TO("=="), GREATER_OR_EQUAL(">="), GREATER_THAN(">");
153 |
154 | private final String display;
155 |
156 | private ComparisonOperator(final String display) {
157 | this.display = display;
158 | }
159 |
160 | @Override
161 | public String toString() {
162 | return display;
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/workflow/condition/NoOpenReviews.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.workflow.condition;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
17 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
18 |
19 | import com.atlassian.jira.issue.Issue;
20 | import com.atlassian.jira.workflow.condition.AbstractJiraCondition;
21 | import com.opensymphony.module.propertyset.PropertySet;
22 | import com.opensymphony.workflow.WorkflowException;
23 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
24 |
25 | import java.util.List;
26 | import java.util.Map;
27 |
28 | /**
29 | * Workflow condition that can be used to enforce that an issue "MUST", or "MUST NOT" have any open
30 | * Gerrit reviews.
31 | *
32 | * @author Joe Hansche
33 | */
34 | public class NoOpenReviews extends AbstractJiraCondition {
35 | public static final String KEY_REVERSED = "reversed";
36 |
37 | private IssueReviewsManager reviewsManager;
38 |
39 | public NoOpenReviews(IssueReviewsManager reviewsManager) {
40 | this.reviewsManager = reviewsManager;
41 | }
42 |
43 | @Override
44 | public boolean passesCondition(@SuppressWarnings("rawtypes") Map transientVars, @SuppressWarnings("rawtypes") Map args, PropertySet ps)
45 | throws WorkflowException {
46 | Issue issue = getIssue(transientVars);
47 | List reviews;
48 |
49 | try {
50 | reviews = reviewsManager.getReviewsForIssue(issue);
51 | } catch (GerritQueryException e) {
52 | // If there's an error, best not to block the workflow, and just act like it passes??
53 | throw new WorkflowException(e);
54 | }
55 |
56 | String value = (String) args.get(KEY_REVERSED);
57 | boolean isReversed = Boolean.parseBoolean(value);
58 |
59 | // The ReviewsManager will only return issues that are "status:open" by default.
60 | int numOpenReviews = countReviewStatus(reviews, true);
61 |
62 | return isReversed ? numOpenReviews > 0 : numOpenReviews == 0;
63 | }
64 |
65 | /**
66 | * Counts the number of reviews that are open or closed.
67 | *
68 | * @param reviews a set of Gerrit changes
69 | * @param isOpen {@code true} to count all open reviews, {@code false} to
70 | * count all non-open reviews.
71 | * @return the number of changes within {@code reviews} that match the
72 | * {@code isOpen} flag.
73 | */
74 | public static int countReviewStatus(List reviews, boolean isOpen) {
75 | int count = 0;
76 |
77 | for (GerritChange change : reviews) {
78 | if (change.isOpen() == isOpen) {
79 | count += 1;
80 | }
81 | }
82 |
83 | return count;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/meetme/plugins/jira/gerrit/workflow/function/ApprovalFunction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.meetme.plugins.jira.gerrit.workflow.function;
15 |
16 | import com.meetme.plugins.jira.gerrit.data.GerritConfiguration;
17 | import com.meetme.plugins.jira.gerrit.data.IssueReviewsManager;
18 | import com.meetme.plugins.jira.gerrit.data.dto.GerritChange;
19 | import com.meetme.plugins.jira.gerrit.workflow.condition.ApprovalScore;
20 |
21 | import com.atlassian.core.user.preferences.Preferences;
22 | import com.atlassian.jira.issue.Issue;
23 | import com.atlassian.jira.user.ApplicationUser;
24 | import com.atlassian.jira.user.preferences.UserPreferencesManager;
25 | import com.atlassian.jira.workflow.function.issue.AbstractJiraFunctionProvider;
26 | import com.opensymphony.module.propertyset.PropertySet;
27 | import com.opensymphony.workflow.WorkflowException;
28 | import com.sonyericsson.hudson.plugins.gerrit.gerritevents.GerritQueryException;
29 |
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | import java.io.IOException;
34 | import java.util.List;
35 | import java.util.Map;
36 |
37 | /**
38 | * A Workflow Function that can be used to perform Gerrit approvals as the result of a workflow
39 | * transition. The input argument is simply a command line argument string (such as "--verified +1",
40 | * or "--submit", etc). The argument will be appended to the gerrit review [ChangeId] ...
41 | * command line.
42 | *
43 | * This function can be used in combination with {@link ApprovalScore} workflow conditions, such
44 | * that, e.g., a "Merge Change" workflow transition can be used to automatically "submit" a Gerrit
45 | * review, iff all of the following conditions are met:
46 | *
47 | *
MUST have a Code-Review score >= 2
48 | *
MUST have a Verified score >= 1
49 | *
Must NOT have a Code-Review score < 0
50 | *
51 | *
52 | * This ensures that the workflow transition is only available if the "submit" step will be
53 | * successful.
54 | *
55 | * Another common use for this function would be to automatically provide a "Verified +1" score, via
56 | * another workflow step, e.g., "Ready for Merge". In that way, a "Ready for Merge" transition may
57 | * then automatically enable the "Merge Change" transition, as a result of giving the Verified +1
58 | * score.
59 | *
60 | * @author Joe Hansche
61 | */
62 | public class ApprovalFunction extends AbstractJiraFunctionProvider {
63 | private static final Logger log = LoggerFactory.getLogger(ApprovalFunction.class);
64 |
65 | public static final String KEY_CMD_ARGS = "cmdArgs";
66 | public static final String DEFAULT_CMD_ARGS = "--verified 1 --submit";
67 |
68 | private final IssueReviewsManager reviewsManager;
69 | private final GerritConfiguration configuration;
70 | private final UserPreferencesManager prefsManager;
71 |
72 | public ApprovalFunction(GerritConfiguration configuration, IssueReviewsManager reviewsManager, UserPreferencesManager prefsManager) {
73 | super();
74 |
75 | this.configuration = configuration;
76 | this.reviewsManager = reviewsManager;
77 | this.prefsManager = prefsManager;
78 | }
79 |
80 | @Override
81 | public void execute(@SuppressWarnings("rawtypes") Map transientVars, @SuppressWarnings("rawtypes") Map args, PropertySet ps)
82 | throws WorkflowException {
83 | if (!isConfigurationReady()) {
84 | throw new IllegalStateException("Configure the Gerrit integration from the Administration panel first.");
85 | }
86 |
87 | final Issue issue = getIssue(transientVars);
88 | final List issueReviews = getReviews(issue);
89 | final Preferences prefs = getUserPrefs(transientVars, args);
90 | final String cmdArgs = (String) args.get(KEY_CMD_ARGS);
91 |
92 | boolean success = false;
93 |
94 | try {
95 | success = reviewsManager.doApprovals(issue, issueReviews, cmdArgs, prefs);
96 | } catch (IOException e) {
97 | throw new WorkflowException("An error occurred while approving the changes", e);
98 | }
99 |
100 | if (!success) {
101 | log.warn("doApprovals() returned false!");
102 | // throw new WorkflowException("Gerrit failed to perform the approvals!");
103 | }
104 | }
105 |
106 | protected Preferences getUserPrefs(@SuppressWarnings("rawtypes") Map transientVars, @SuppressWarnings("rawtypes") Map args) {
107 | final ApplicationUser user = getCaller(transientVars, args);
108 | return prefsManager.getPreferences(user);
109 | }
110 |
111 | protected String getIssueKey(@SuppressWarnings("rawtypes") Map transientVars) {
112 | return getIssue(transientVars).getKey();
113 | }
114 |
115 | protected List getReviews(Issue issue) throws WorkflowException {
116 | try {
117 | return reviewsManager.getReviewsForIssue(issue);
118 | } catch (GerritQueryException e) {
119 | throw new WorkflowException("Unable to retrieve associated reviews", e);
120 | }
121 | }
122 |
123 | protected boolean isConfigurationReady() {
124 | return configuration != null && configuration.getSshHostname() != null && configuration.getSshUsername() != null
125 | && configuration.getSshPrivateKey() != null && configuration.getSshPrivateKey().exists();
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/resources/i18n/admin.properties:
--------------------------------------------------------------------------------
1 | # Copyright 2012 MeetMe, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | gerrit.admin.label=Gerrit Settings
16 |
17 | gerrit.admin.ssh.label=SSH Connection
18 | gerrit.admin.host.label=SSH Host
19 | gerrit.admin.host.description=Example: gerrit.company.com
20 | gerrit.admin.port.label=SSH Port
21 | gerrit.admin.port.description=Example: 29418
22 | gerrit.admin.username.label=SSH Username
23 | gerrit.admin.username.description=Example: jira
24 | gerrit.admin.sshKey.label=SSH Private Key
25 | gerrit.admin.sshKey.description=Example: id_rsa file
26 | gerrit.admin.sshKey.isOnFile=Private Key is already on file. Upload a new one to replace it.
27 | gerrit.admin.sshKey.missing=A private key is required!
28 |
29 | gerrit.admin.search.label=Search Queries
30 | gerrit.admin.issueSearchQuery.label=Issue Search
31 | gerrit.admin.issueSearchQuery.description=Enter a search query used to locate reviews for a given issue (via gerrit query [...]). Use "%s" as a placeholder for the issue key. To reference the issue key multiple times in the query, use "%1$s" instead. \
32 | Examples: "tr:%s"; "topic:%s"; "message:%s"; "tr:%s OR topic:%1$s"; etc.
33 | gerrit.admin.projectSearchQuery.label=Project Search
34 | gerrit.admin.projectSearchQuery.description=Enter a search query used to locate reviews for a given project (via gerrit query [...]). Use "%s" or "%1$s" as a placeholder for the project key. \
35 | Examples: project:%s, topic:%s, or message:%s-*.
36 |
37 | ## Not yet implemented (JSON-RPC):
38 | gerrit.admin.http.label=HTTP Settings (optional)
39 | gerrit.admin.httpBaseUrl.label=HTTP Base URL
40 | gerrit.admin.httpBaseUrl.description=Example: http://gerrit.company.com/
41 | gerrit.admin.httpUsername.label=HTTP Username
42 | gerrit.admin.httpUsername.description=Example: jira
43 | gerrit.admin.httpPassword.label=HTTP Password
44 |
45 | gerrit.admin.project.settings=Project Settings
46 | gerrit.admin.showEmptyPanel.label=Show Gerrit Reviews panel even if there are no reviews
47 | gerrit.admin.showEmptyPanel.description=If there are no matching Gerrit reviews for the issue, the panel will still display with a note that nothing matches. \
48 | Uncheck this box to remove the panel instead.
49 | gerrit.admin.project.whitelist=Gerrit Projects Whitelist
50 | gerrit.admin.project.whitelist.description=List of projects, that use gerrit.
51 | gerrit.admin.project.useWhiteList.label=Use Gerrit Project Whitelist
52 | gerrit.admin.project.useWhiteList.description=If a list of projects, that use Gerrit, shall be maintained.
--------------------------------------------------------------------------------
/src/main/resources/i18n/tabpanel.properties:
--------------------------------------------------------------------------------
1 | # Copyright 2012 MeetMe, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | gerrit.tabpanel.label=Gerrit Reviews
16 | gerrit.tabpanel.Review=Gerrit Review
17 | gerrit.tabpanel.Project=Project
18 | gerrit.tabpanel.Branch=Branch
19 | gerrit.tabpanel.Approver=Approver
20 | gerrit.tabpanel.Type=Type
21 | gerrit.tabpanel.Score=Score
22 | gerrit.tabpanel.Status=Status
23 | gerrit.tabpanel.status.open=Open
24 | gerrit.tabpanel.status.closed=Closed
25 | gerrit.tabpanel.permalink=A permanent link to this Gerrit change
26 | gerrit.tabpanel.no_approvals=There are no approvals yet for this Gerrit change.
27 | gerrit.tabpanel.no_changes=There are no open Gerrit changes for this issue.
28 |
29 | gerrit.tabpanel.most_significant_score=This is the most significant approval score for the Gerrit change.
30 |
31 | gerrit.tabpanel.subtasks.label=Subtask Gerrit Reviews
32 | gerrit.tabpanel.subtasks.permalink=A permanent link to this Sub-Task
33 | gerrit.tabpanel.subtasks.has_open_reviews=There {0,choice, 0#are no| 1#is 1| 1zero open Gerrit review.
25 | gerrit.workflow.no-open-reviews.view.reversed-true=The issue must have at least one open Gerrit reviews.
26 |
27 |
28 | ## Approval Score condition
29 | gerrit.workflow.score-condition.label=Gerrit Approval Score Condition
30 |
31 | gerrit.workflow.score-condition.edit.negative.label=Logic
32 | gerrit.workflow.score-condition.edit.negative.description=Whether the condition MUST or MUST NOT be met.
33 | gerrit.workflow.score-condition.edit.comparison.label=Comparison Operator
34 | gerrit.workflow.score-condition.edit.comparison.description=Choose an operator to compare the actual score to the target score. E.g., ACTUAL <operator> TARGET
35 | gerrit.workflow.score-condition.edit.target-score.label=Target Approval Score
36 | gerrit.workflow.score-condition.edit.target-score.description=The target approval score to compare actual approvals against.
37 | gerrit.workflow.score-condition.edit.approval-category.label=Approval Type
38 | gerrit.workflow.score-condition.edit.approval-category.description=The internal approval category label. E.g., Code-Review, Verified, etc.
39 | # Select drop-down options for $negative
40 | gerrit.workflow.score-condition.edit.negative-false.label=MUST
41 | gerrit.workflow.score-condition.edit.negative-true.label=Must NOT
42 |
43 | #args: [$comparison, $target, $label]
44 | gerrit.workflow.score-condition.view.positive=MUST have approval score {0} {1} for category {2}
45 | gerrit.workflow.score-condition.view.negative=Must NOT have approval score {0} {1} for category {2}
46 |
47 |
48 | ## Modify Gerrit review, function
49 | gerrit.workflow.approve.label=Approve Gerrit Review
50 |
51 | gerrit.workflow.approve.edit.cmdArgs.label=Command Arguments
52 | gerrit.workflow.approve.edit.cmdArgs.description=Enter the command arguments to pass to the gerrit review ... command. For example: --verified 1; or --submit
53 |
54 | gerrit.workflow.approve.view=Approve all Gerrit reviews with: gerrit review [ChangeId] {0}
55 |
--------------------------------------------------------------------------------
/src/main/resources/images/gerrit-check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MeetMe/jira-gerrit-plugin/a0be628cc1ad676235cef1ab174fbeb12ddb45ed/src/main/resources/images/gerrit-check.png
--------------------------------------------------------------------------------
/src/main/resources/images/gerrit-icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MeetMe/jira-gerrit-plugin/a0be628cc1ad676235cef1ab174fbeb12ddb45ed/src/main/resources/images/gerrit-icon16.png
--------------------------------------------------------------------------------
/src/main/resources/images/gerrit-x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MeetMe/jira-gerrit-plugin/a0be628cc1ad676235cef1ab174fbeb12ddb45ed/src/main/resources/images/gerrit-x.png
--------------------------------------------------------------------------------
/src/main/resources/images/meetme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MeetMe/jira-gerrit-plugin/a0be628cc1ad676235cef1ab174fbeb12ddb45ed/src/main/resources/images/meetme.png
--------------------------------------------------------------------------------
/src/main/resources/images/meetme_75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MeetMe/jira-gerrit-plugin/a0be628cc1ad676235cef1ab174fbeb12ddb45ed/src/main/resources/images/meetme_75.png
--------------------------------------------------------------------------------
/src/main/resources/styles/gerrit-reviews-tabpanel.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 MeetMe, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | .module table.gerrit-review-approvals
15 | {
16 | width: 400px;
17 | }
18 |
19 | table td.gerrit-review-score
20 | {
21 | text-align: right;
22 | }
23 |
24 | table.gerrit-review-approvals td.review-negative,
25 | div.flooded span.review-negative,
26 | .gerrit-review-score.review-negative
27 | {
28 | color: #f00;
29 | }
30 |
31 | table.gerrit-review-approvals td.review-positive,
32 | div.flooded span.review-positive,
33 | .gerrit-review-score.review-positive
34 | {
35 | color: #08A400;
36 | }
37 |
38 | .gerrit-status-ABANDONED span.gerrit-review
39 | {
40 | background-image: url(images/gerrit-x.png);
41 | }
42 |
43 | .gerrit-status-MERGED span.gerrit-review,
44 | .gerrit-status-SUBMITTED span.gerrit-review
45 | {
46 | background-image: url(images/gerrit-check.png);
47 | }
48 |
49 | span.gerrit-review
50 | {
51 | display: inline-block;
52 | text-indent: -9999px;
53 | width: 16px;
54 | height: 16px;
55 | background-color: transparent;
56 | background-image: url(images/gerrit-icon16.png);
57 | background-repeat: no-repeat;
58 | background-position: 50% 50%;
59 | }
60 |
61 | span.gerrit-open-reviews
62 | {
63 | color: #900;
64 | }
65 |
66 | span.gerrit-no-reviews
67 | {
68 | font-style: italic;
69 | }
70 |
71 | #gerrit-reviews-side-panel ul.item-details dl dt
72 | {
73 | white-space: nowrap;
74 | overflow: hidden;
75 | text-overflow: none;
76 | width: 81%;
77 | text-align: left;
78 | }
79 |
80 | #gerrit-reviews-side-panel ul.item-details dl dd
81 | {
82 | width: 18%;
83 | text-align: right;
84 | }
85 |
86 | #gerrit-reviews-side-panel ul.item-details dl dd span.gerrit-review
87 | {
88 | text-indent: 9999px;
89 | vertical-align: middle;
90 | overflow: hidden;
91 | }
92 |
93 | #gerrit-reviews-side-panel span.twixi
94 | {
95 | cursor: pointer
96 | }
97 |
98 | #gerrit-reviews-left-panel
99 | {
100 | width: 100%;
101 | margin: 0 0 8px 0;
102 | background-color: white;
103 | border-collapse: collapse;
104 | text-align: left;
105 | }
106 |
107 | #gerrit-reviews-left-panel tr.issuerow:hover
108 | {
109 | background-color: #f0f0f0;
110 | }
111 |
112 | #gerrit-reviews-left-panel tr td.gerrit-subject
113 | {
114 | min-width: 200px;
115 | margin: 0;
116 | max-width: 1400px;
117 | white-space: normal;
118 | }
119 |
120 | #gerrit-reviews-left-panel td
121 | {
122 | height: 25px;
123 | line-height: 1.286;
124 | padding: 5px 7px 0 0;
125 | overflow: hidden;
126 | }
127 |
128 | #gerrit-reviews-left-panel .nav
129 | {
130 | border-bottom: 1px solid #eee;
131 | vertical-align: middle;
132 | }
133 |
134 | #gerrit-reviews-left-panel img
135 | {
136 | vertical-align: middle;
137 | }
138 |
139 | #gerrit-reviews-left-panel .gerrit-changeid,
140 | #gerrit-reviews-left-panel .gerrit-patchset
141 | {
142 | white-space: nowrap;
143 | width: 16px;
144 | }
145 |
146 | #gerrit-reviews-left-panel .gerrit-status,
147 | #gerrit-reviews-left-panel .gerrit-review-score
148 | {
149 | white-space: nowrap;
150 | width: 16px;
151 | max-width: 150px;
152 | }
153 |
154 | #gerrit-reviews-left-panel th.dashboardHeader
155 | {
156 | font-weight: normal;
157 | text-align: left;
158 | }
159 |
160 | #gerrit-reviews-left-panel td.gerrit-review-score
161 | {
162 | text-align: left;
163 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/gerrit-reviews-agile-panel.vm:
--------------------------------------------------------------------------------
1 |
72 |