JiraClient.java
package access.jira;
import access.mail.MailBox;
import access.remote.RestTemplateFactory;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
@EnableConfigurationProperties(JiraConfig.class)
@Service
public class JiraClient {
private static final Logger LOG = LoggerFactory.getLogger(JiraClient.class);
private final JiraConfig config;
private final MailBox mailBox;
private final Map<String, Map<String, Map<String, String>>> mappings;
private final String issueType;
private RestTemplate restTemplate;
@SneakyThrows
@SuppressWarnings("unchcked")
public JiraClient(JiraConfig config, ObjectMapper objectMapper, MailBox mailBox) {
this.config = config;
this.mailBox = mailBox;
this.mappings = objectMapper.readValue(new ClassPathResource("jira/mappings.json").getInputStream(), new TypeReference<>() {
});
this.issueType = this.resolveIssueType();
if (config.isEnabled()) {
this.restTemplate = RestTemplateFactory.buildRestTemplate(config.getApiKey());
}
}
@SneakyThrows
@SuppressWarnings("unchecked")
public String create(JiraIssue issue) {
if (!config.isEnabled()) {
return String.format("CXT-%s", ThreadLocalRandom.current().nextInt(1000, 10000));
}
Map<String, Object> fields = new HashMap<>();
fields.put("project", Map.of("key", config.getProjectKey()));
fields.put("customfield_" + spCustomField(), issue.getServiceProviderEntityID());
fields.put("customfield_" + idpCustomField(), issue.getIdentityProviderEntityID());
fields.put("customfield_" + typeMetaDataCustomField(), Map.of("value", issue.getEntityType().name()));
fields.put("customfield_" + emailToCustomField(), issue.getEmailTo());
fields.put("issuetype", ImmutableMap.of("id", issueType));
fields.put("summary", issue.getSummary());
fields.put("description", issue.getDescription());
fields.put("duedate", dueDate());
//We don't send keys with null or empty values
fields.entrySet().removeIf(entry -> entry.getValue() instanceof String && !StringUtils.hasText((String) entry.getValue()));
Map<String, Map<String, Object>> jiraIssue = Map.of("fields", fields);
LOG.info("Sending JSON {} to JIRA", jiraIssue);
try {
Map<String, String> result = restTemplate.postForObject(config.getBaseUrl() + "/issue", jiraIssue, Map.class);
LOG.info("Response {} from JIRA", result);
return result.get("key");
} catch (HttpClientErrorException e) {
LOG.error("Failed to create Jira issue: {} ({}) with response:{}, JSON Request: {}",
e.getStatusCode(),
e.getStatusText(),
e.getResponseBodyAsString(),
jiraIssue,
e);
mailBox.sendJiraError("create", jiraIssue.toString(), e.getMessage(), e.getResponseBodyAsString());
throw e;
}
}
public void comment(String jiraKey, String comment) {
if (!config.isEnabled()) {
return;
}
String commentUrl = config.getBaseUrl() + "/issue/" + jiraKey + "/comment";
Map<String, String> body = Map.of("body", comment);
HttpEntity<Object> commentRequestEntity = new HttpEntity<>(body);
LOG.info("Sending JSON {} to JIRA", body);
try {
ResponseEntity<Map> responseEntity = restTemplate.exchange(commentUrl, HttpMethod.POST, commentRequestEntity, Map.class);
LOG.info("Response {} from JIRA", responseEntity.getBody());
} catch (HttpClientErrorException e) {
LOG.error("Failed to post Jira comment: {} ({}) with response:{}, JSON Request: {}",
e.getStatusCode(),
e.getStatusText(),
e.getResponseBodyAsString(),
body,
e);
mailBox.sendJiraError("comment", body.toString(), e.getMessage(), e.getResponseBodyAsString());
throw e;
}
}
private String resolveIssueType() {
return this.mappings.get(this.config.getEnvironment())
.get("issueTypes").entrySet().stream()
.filter(entry -> entry.getKey().equals("change"))
.map(entry -> entry.getValue())
.findFirst().get();
}
private String spCustomField() {
return this.customField("spEntityId");
}
private String idpCustomField() {
return this.customField("idpEntityId");
}
private String typeMetaDataCustomField() {
return this.customField("typeMetaData");
}
private String emailToCustomField() {
return this.customField("emailTo");
}
private String customField(String name) {
return this.mappings.get(this.config.getEnvironment()).get("customFields").get(name);
}
private String dueDate() {
LocalDate localDate = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
return localDate.plusWeeks(3).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}