-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Base Ability to Have Custom Fields #153
Changes from 8 commits
3e1d3d5
beb659e
3de460f
1cd6f98
f8cb0f7
8a858eb
22d830f
e4818da
e08e1fc
92a7e08
30535c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,8 @@ couscous.phar | |
.idea | ||
classes | ||
*.iml | ||
.vscode | ||
.sdkmanrc | ||
|
||
/.nb-gradle/ | ||
*.received.txt | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,44 @@ | ||
package se.bjurr.gitchangelog.internal.integrations.jira; | ||
|
||
import static com.jayway.jsonpath.JsonPath.read; | ||
import static org.slf4j.LoggerFactory.getLogger; | ||
|
||
import com.jayway.jsonpath.JsonPath; | ||
import com.jayway.jsonpath.PathNotFoundException; | ||
import java.io.UnsupportedEncodingException; | ||
import java.net.URLEncoder; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
import org.slf4j.Logger; | ||
import se.bjurr.gitchangelog.api.exceptions.GitChangelogIntegrationException; | ||
import se.bjurr.gitchangelog.internal.settings.SettingsJiraIssueFieldFilter; | ||
|
||
public abstract class JiraClient { | ||
private static final String SEARCH_API = "/search?jql=issue="; | ||
private static final String ISSUE_API = "/issue/"; | ||
private static final String ISSUE_API_FIELD_PREFIX = "$.fields."; | ||
private static final String SEARCH_API_FIELD_PREFIX = "$.issues[0].fields."; | ||
private static final String AND = " AND "; | ||
private static final String SINGLE_QUOTE = "'"; | ||
private static final String COMMA = ","; | ||
private static final String QUESTION_MARK = "?"; | ||
private static final String AMPERSAND = "&"; | ||
private static final String EMPTY_STRING = ""; | ||
private static final String DEFAULT_FIELDS = | ||
"fields=parent,summary,issuetype,labels,description,issuelinks"; | ||
private static final String BASE_PATH = "/rest/api/2"; | ||
private static final Logger LOG = getLogger(JiraClient.class); | ||
|
||
private final String api; | ||
private List<String> fields = Collections.unmodifiableList(new ArrayList<>()); | ||
private List<SettingsJiraIssueFieldFilter> filters = | ||
Collections.unmodifiableList(new ArrayList<>()); | ||
|
||
public JiraClient(final String api) { | ||
if (api.endsWith("/")) { | ||
|
@@ -25,30 +52,106 @@ public String getApi() { | |
return this.api; | ||
} | ||
|
||
protected String getIssuePath() { | ||
return this.hasIssueFieldFilters() ? SEARCH_API : ISSUE_API; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you not always use search API? When can you use it? When can you not use it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe you would use |
||
} | ||
|
||
protected String getEndpoint(final String issue) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this method should me unit-tested. |
||
final String endpoint = | ||
this.api | ||
+ "/rest/api/2/issue/" | ||
+ issue | ||
+ "?fields=parent,summary,issuetype,labels,description,issuelinks"; | ||
return endpoint; | ||
return this.api | ||
+ BASE_PATH | ||
+ getIssuePath() | ||
+ issue | ||
+ (hasIssueFieldFilters() ? getIssueFieldFiltersQuery() + AMPERSAND : QUESTION_MARK) | ||
+ DEFAULT_FIELDS | ||
+ (hasIssueAdditionalFields() ? COMMA + getIssueAdditionalFieldsQuery() : EMPTY_STRING); | ||
} | ||
|
||
private boolean hasIssueFieldFilters() { | ||
return this.filters != null && !filters.isEmpty(); | ||
} | ||
|
||
private String getIssueFieldFiltersQuery() { | ||
final StringBuffer queryBuffer = new StringBuffer(); | ||
|
||
for (SettingsJiraIssueFieldFilter filter : filters) { | ||
queryBuffer.append( | ||
AND | ||
+ filter.getKey() | ||
+ filter.getOperator() | ||
+ SINGLE_QUOTE | ||
+ filter.getValue() | ||
+ SINGLE_QUOTE); | ||
} | ||
try { | ||
return URLEncoder.encode(queryBuffer.toString(), "UTF-8"); | ||
} catch (UnsupportedEncodingException e) { | ||
return ""; | ||
} | ||
} | ||
|
||
public JiraClient withIssueAdditionalFields(final List<String> fields) { | ||
this.fields = fields; | ||
return this; | ||
} | ||
|
||
public JiraClient withIssueFieldFilters(final List<SettingsJiraIssueFieldFilter> filters) { | ||
this.filters = filters; | ||
return this; | ||
} | ||
|
||
private boolean hasIssueAdditionalFields() { | ||
return fields != null && !fields.isEmpty(); | ||
} | ||
|
||
private String getIssueAdditionalFieldsQuery() { | ||
return String.join(COMMA, fields); | ||
} | ||
|
||
private String getFieldPrefix() { | ||
return this.hasIssueFieldFilters() ? SEARCH_API_FIELD_PREFIX : ISSUE_API_FIELD_PREFIX; | ||
} | ||
|
||
protected JiraIssue toJiraIssue(final String issue, final String json) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this method should me unit-tested. |
||
final String title = read(json, "$.fields.summary"); | ||
final String description = read(json, "$.fields.description"); | ||
final String type = read(json, "$.fields.issuetype.name"); | ||
final String fieldPrefix = getFieldPrefix(); | ||
|
||
final String title = read(json, fieldPrefix + "summary"); | ||
final String description = read(json, fieldPrefix + "description"); | ||
final String type = read(json, fieldPrefix + "issuetype.name"); | ||
final String link = this.api + "/browse/" + issue; | ||
final List<String> labels = JsonPath.read(json, "$.fields.labels"); | ||
final List<String> labels = read(json, fieldPrefix + "labels"); | ||
final List<String> linkedIssues = new ArrayList<>(); | ||
final List<String> inwardKey = JsonPath.read(json, "$.fields.issuelinks[*].inwardIssue.key"); | ||
final List<String> outwardKey = JsonPath.read(json, "$.fields.issuelinks[*].outwardIssue.key"); | ||
final List<String> inwardKey = read(json, fieldPrefix + "issuelinks[*].inwardIssue.key"); | ||
final List<String> outwardKey = read(json, fieldPrefix + "issuelinks[*].outwardIssue.key"); | ||
linkedIssues.addAll(inwardKey); | ||
linkedIssues.addAll(outwardKey); | ||
|
||
final JiraIssue jiraIssue = | ||
new JiraIssue(title, description, link, issue, type, linkedIssues, labels); | ||
return jiraIssue; | ||
final Map<String, Object> additionalFields = | ||
fields.stream() | ||
.reduce( | ||
(Map<String, Object>) new HashMap<String, Object>(), | ||
(fields, field) -> getAdditionalField(json, fieldPrefix, fields, field), | ||
(leftSide, rightSide) -> | ||
Stream.of(leftSide, rightSide) | ||
.map(Map::entrySet) | ||
.flatMap(Collection::stream) | ||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); | ||
|
||
return new JiraIssue( | ||
title, description, link, issue, type, linkedIssues, labels, additionalFields); | ||
} | ||
|
||
private Map<String, Object> getAdditionalField( | ||
final String json, | ||
final String fieldPrefix, | ||
final Map<String, Object> additionalFields, | ||
final String additionalFieldString) { | ||
try { | ||
additionalFields.put(additionalFieldString, read(json, fieldPrefix + additionalFieldString)); | ||
} catch (PathNotFoundException e) { | ||
LOG.warn("Could not find the additional field", e); | ||
} | ||
|
||
return additionalFields; | ||
} | ||
|
||
public abstract JiraClient withBasicCredentials(String username, String password); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand this feature. Are you looking for issues that are not mentioned in the commit message?
When would I want to use this method and what would be the result?
Perhaps these are 2 methods are 2 completely separate features? In that case it might be easier to understand the PR if it is split into 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great question haha this feature is also not one that I ever need for my team's workflow, but according to the original author of #97:
That made kinda sense to me, maybe? I believe you would use
search
vsissue
for when you are wanting an issue returned to potentially be skipped. I tested this w/ Postman by adding essentially anassignee = 'me@email.com'
and seeing the ticket either showing up or not.I am with you that this can be separated into two different PRs, essentially a 'fixed' #97 and then the "additional fields" work I added on top. Since my stuff is what's important to me, this PR will be rebased to only include the additional fields.