package slacknotifications.teamcity.payload.content;
import jetbrains.buildServer.BuildProblemData;
import jetbrains.buildServer.serverSide.*;
import jetbrains.buildServer.tests.TestInfo;
import jetbrains.buildServer.users.SUser;
import jetbrains.buildServer.vcs.SVcsModification;
import slacknotifications.teamcity.BuildStateEnum;
import slacknotifications.teamcity.Loggers;
import slacknotifications.teamcity.SlackNotificator;
import slacknotifications.teamcity.TeamCityIdResolver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class SlackNotificationPayloadContent {
String buildStatus;
String buildResult;
String buildResultPrevious;
String buildResultDelta;
String notifyType;
String buildFullName;
String buildName;
String buildId;
String buildTypeId;
String buildInternalTypeId;
String buildExternalTypeId;
String buildStatusUrl;
String buildStatusHtml;
String rootUrl;
String projectName;
String projectId;
String projectInternalId;
String projectExternalId;
String buildNumber;
String agentName;
String agentOs;
String agentHostname;
String triggeredBy;
String comment;
String message;
String text;
String branchName;
String branchDisplayName;
String buildStateDescription;
String progressSummary;
List<Commit> commits;
private boolean isFirstFailedBuild;
Boolean branchIsDefault;
Branch branch;
/*
public final static String BUILD_STATUS_FAILURE = "failure";
public final static String BUILD_STATUS_SUCCESS = "success";
public final static String BUILD_STATUS_RUNNING = "running";
public final static String BUILD_STATUS_NO_CHANGE = "unchanged";
public final static String BUILD_STATUS_FIXED = "fixed";
public final static String BUILD_STATUS_BROKEN = "broken";
public final static String BUILD_STATUS_UNKNOWN = "unknown";*/
public final static String BUILD_STATUS_FAILURE = "Failed";
public final static String BUILD_STATUS_SUCCESS = "Succeeded";
public final static String BUILD_STATUS_RUNNING = "Running";
public final static String BUILD_STATUS_NO_CHANGE = "Unchanged";
public final static String BUILD_STATUS_FIXED = "Fixed";
public final static String BUILD_STATUS_BROKEN = "Broken";
public final static String BUILD_STATUS_UNKNOWN = "Unknown";
private String buildLink;
private String color;
private long elapsedTime;
private boolean isComplete;
private ArrayList<String> failedBuildMessages = new ArrayList<String>();
private ArrayList<String> failedTestNames = new ArrayList<String>();
public SlackNotificationPayloadContent(){
}
/**
* Constructor: Only called by RepsonsibilityChanged.
* @param server
* @param buildType
* @param buildState
*/
public SlackNotificationPayloadContent(SBuildServer server, SBuildType buildType, BuildStateEnum buildState) {
populateCommonContent(server, buildType, buildState);
}
/**
* Constructor: Called by everything except RepsonsibilityChanged.
* @param server
* @param sRunningBuild
* @param previousBuild
* @param buildState
*/
public SlackNotificationPayloadContent(SBuildServer server, SRunningBuild sRunningBuild, SFinishedBuild previousBuild,
BuildStateEnum buildState) {
this.commits = new ArrayList<Commit>();
populateCommonContent(server, sRunningBuild, previousBuild, buildState);
populateMessageAndText(sRunningBuild, buildState);
populateCommits(sRunningBuild);
populateArtifacts(sRunningBuild);
populateResults(sRunningBuild);
}
private void populateResults(SRunningBuild sRunningBuild) {
List<BuildProblemData> failureReasons = sRunningBuild.getFailureReasons();
if(failureReasons == null){
return;
}
HashSet<String> failureTestNames = new HashSet<String>();
HashSet<String> failureMessages = new HashSet<String>();
for(BuildProblemData reason : failureReasons){
if(reason.getType() == BuildProblemData.TC_FAILED_TESTS_TYPE){
List<TestInfo> failedTestMessages = sRunningBuild.getTestMessages(0, 2000);
if(!failedTestMessages.isEmpty()) {
for (TestInfo failedTest : failedTestMessages) {
failureTestNames.add(failedTest.getName());
}
}
else {
failureMessages.add(reason.getDescription());
}
}
else {
failureMessages.add(reason.getDescription());
}
}
failedBuildMessages = new ArrayList<String>(failureMessages);
failedTestNames = new ArrayList<String>(failureTestNames);
}
private void populateCommits(SRunningBuild sRunningBuild) {
List<SVcsModification> changes = sRunningBuild.getContainingChanges();
if(changes == null){
return;
}
for(SVcsModification change : changes){
Collection<SUser> committers = change.getCommitters();
String slackUserName = null;
if(committers != null && !committers.isEmpty()){
SUser committer = committers.iterator().next();
slackUserName = committer.getPropertyValue(SlackNotificator.USERNAME_KEY);
Loggers.ACTIVITIES.debug("Resolved committer " + change.getUserName() + " to Slack User " + slackUserName);
}
commits.add(new Commit(change.getVersion(), change.getDescription(), change.getUserName(), slackUserName));
}
}
private void populateArtifacts(SRunningBuild runningBuild) {
//ArtifactsInfo artInfo = new ArtifactsInfo(runningBuild);
//artInfo.
}
/**
* Used by RepsonsiblityChanged.
* Therefore, does not have access to a specific build instance.
* @param server
* @param buildType
* @param state
*/
private void populateCommonContent(SBuildServer server, SBuildType buildType, BuildStateEnum state) {
setBuildFullName(buildType.getFullName());
setBuildName(buildType.getName());
setBuildTypeId(TeamCityIdResolver.getBuildTypeId(buildType));
setBuildStatusUrl(server.getRootUrl() + "/viewLog.html?buildTypeId=" + buildType.getBuildTypeId() + "&buildId=lastFinished");
}
private void populateMessageAndText(SRunningBuild sRunningBuild,
BuildStateEnum state) {
// Message is a long form message, for on webpages or in email.
// Text is designed to be shorter, for use in Text messages and the like.
setText(getBuildDescriptionWithLinkSyntax()
+ " has " + state.getDescriptionSuffix() + ". Status: " + this.buildResult);
}
/**
* Used by everything except ResponsibilityChanged. Is passed a valid build instance.
*
* @param server
* @param sRunningBuild
* @param previousBuild
* @param buildState
*/
private void populateCommonContent(SBuildServer server, SRunningBuild sRunningBuild, SFinishedBuild previousBuild,
BuildStateEnum buildState) {
setBuildResult(sRunningBuild, previousBuild, buildState);
setBuildFullName(sRunningBuild.getBuildType().getFullName());
setBuildName(sRunningBuild.getBuildType().getName());
setBuildId(Long.toString(sRunningBuild.getBuildId()));
setBuildTypeId(TeamCityIdResolver.getBuildTypeId(sRunningBuild.getBuildType()));
setAgentName(sRunningBuild.getAgentName());
setElapsedTime(sRunningBuild.getElapsedTime());
try {
if (sRunningBuild.getBranch() != null) {
setBranch(sRunningBuild.getBranch());
setBranchDisplayName(getBranch().getDisplayName());
setBranchIsDefault(getBranch().isDefaultBranch());
} else {
Loggers.SERVER.debug("SlackNotificationPayloadContent :: Branch is null. Either feature branch support is not configured or Teamcity does not support feature branches on this VCS");
}
} catch (NoSuchMethodError e) {
Loggers.SERVER.debug("SlackNotificationPayloadContent :: Could not get Branch Info by calling sRunningBuild.getBranch(). Probably an old version of TeamCity");
}
setBuildStatusUrl(server.getRootUrl() + "/viewLog.html?buildTypeId=" + getBuildTypeId() + "&buildId=" + getBuildId());
String branchSuffix = (getBranchIsDefault() != null && getBranchIsDefault()) || getBranchDisplayName() == null ? "" : (" [" + getBranchDisplayName() + "]");
setBuildDescriptionWithLinkSyntax(String.format("<" + getBuildStatusUrl() + "|" + getBuildResult() + " - " + sRunningBuild.getBuildType().getFullName() + " #" + sRunningBuild.getBuildNumber() + branchSuffix + ">"));
}
private Branch getBranch() {
return this.branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
public String getBranchDisplayName() {
return this.branchDisplayName;
}
public void setBranchDisplayName(String displayName) {
this.branchDisplayName = displayName;
}
public Boolean getBranchIsDefault() {
return branchIsDefault;
}
public Boolean isMergeBranch() { return this.branchName != null && this.branchName.endsWith("/merge");}
public void setBranchIsDefault(boolean branchIsDefault) {
this.branchIsDefault = branchIsDefault;
}
/**
* Determines a useful build result. The one from TeamCity can't be trusted because it
* is not set until all the Notifiers have run, of which we are one.
* @param sRunningBuild
* @param previousBuild
* @param buildState
*/
private void setBuildResult(SRunningBuild sRunningBuild,
SFinishedBuild previousBuild, BuildStateEnum buildState) {
if (previousBuild != null){
if (previousBuild.isFinished()){
if (previousBuild.getStatusDescriptor().isSuccessful()){
this.buildResultPrevious = BUILD_STATUS_SUCCESS;
} else {
this.buildResultPrevious = BUILD_STATUS_FAILURE;
}
} else {
this.buildResultPrevious = BUILD_STATUS_RUNNING;
}
} else {
this.buildResultPrevious = BUILD_STATUS_UNKNOWN;
}
isComplete = buildState == BuildStateEnum.BUILD_FINISHED;
if (buildState == BuildStateEnum.BEFORE_BUILD_FINISHED || buildState == BuildStateEnum.BUILD_FINISHED){
if (sRunningBuild.getStatusDescriptor().isSuccessful()){
this.buildResult = BUILD_STATUS_SUCCESS;
this.color = "good";
if (this.buildResultPrevious.equals(this.buildResult)){
this.buildResultDelta = BUILD_STATUS_NO_CHANGE;
} else {
this.buildResultDelta = BUILD_STATUS_FIXED;
}
} else {
this.buildResult = BUILD_STATUS_FAILURE;
this.color = "danger";
if (this.buildResultPrevious.equals(this.buildResult)){
this.buildResultDelta = BUILD_STATUS_NO_CHANGE;
} else {
this.buildResultDelta = BUILD_STATUS_BROKEN;
this.setFirstFailedBuild(true);
}
}
} else {
this.buildResult = BUILD_STATUS_RUNNING;
this.buildResultDelta = BUILD_STATUS_UNKNOWN;
}
}
// Getters and setters
public String getBuildResult() {
return buildResult;
}
public void setBuildResult(String buildResult) {
this.buildResult = buildResult;
}
public String getBuildFullName() {
return buildFullName;
}
public void setBuildFullName(String buildFullName) {
this.buildFullName = buildFullName;
}
public String getBuildName() {
return buildName;
}
public void setBuildName(String buildName) {
this.buildName = buildName;
}
public String getBuildId() {
return buildId;
}
public void setBuildId(String buildId) {
this.buildId = buildId;
}
public String getBuildTypeId() {
return buildTypeId;
}
public void setBuildTypeId(String buildTypeId) {
this.buildTypeId = buildTypeId;
}
public String getAgentName() {
return agentName;
}
public void setAgentName(String agentName) {
this.agentName = agentName;
}
public String getBuildStatusUrl() {
return buildStatusUrl;
}
public void setBuildStatusUrl(String buildStatusUrl) {
this.buildStatusUrl = buildStatusUrl;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void setBuildDescriptionWithLinkSyntax(String buildLink) {
this.buildLink = buildLink;
}
public String getBuildDescriptionWithLinkSyntax() {
return buildLink;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void setElapsedTime(long elapsedTime) {
this.elapsedTime = elapsedTime;
}
public long getElapsedTime() {
return elapsedTime;
}
public List<Commit> getCommits() {
return commits;
}
public void setCommits(List<Commit> commits) {
this.commits = commits;
}
public boolean getIsComplete() {
return isComplete;
}
public void setIsComplete(boolean isComplete) {
this.isComplete = isComplete;
}
public boolean getIsFirstFailedBuild() {
return isFirstFailedBuild;
}
public void setFirstFailedBuild(boolean isFirstFailedBuild) {
this.isFirstFailedBuild = isFirstFailedBuild;
}
public ArrayList<String> getFailedBuildMessages() {
return failedBuildMessages;
}
public ArrayList<String> getFailedTestNames() {
return failedTestNames;
}
}