-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Converted Flowdock XML template to Java template
Fixes #15 Added ability to use ${sanitise(varName)} or ${sanitize(varName)} in a template Fixes #17 Added new project (tcwebhooks-template-builder) to build some templates from XML.
- Loading branch information
Showing
18 changed files
with
798 additions
and
0 deletions.
There are no files selected for viewing
183 changes: 183 additions & 0 deletions
183
.../src/main/java/webhook/teamcity/payload/template/AbstractFileSetBasedWebHookTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package webhook.teamcity.payload.template; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.net.URL; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import webhook.teamcity.BuildStateEnum; | ||
import webhook.teamcity.Loggers; | ||
import webhook.teamcity.payload.WebHookTemplateContent; | ||
import webhook.teamcity.payload.WebHookTemplateManager; | ||
|
||
public abstract class AbstractFileSetBasedWebHookTemplate extends AbstractWebHookTemplate { | ||
|
||
Map<BuildStateEnum,WebHookTemplateContent> templateContent = new HashMap<BuildStateEnum, WebHookTemplateContent>(); | ||
Map<BuildStateEnum,WebHookTemplateContent> branchTemplateContent = new HashMap<BuildStateEnum, WebHookTemplateContent>(); | ||
|
||
public abstract String getLoggingName(); | ||
public abstract String getTemplateFilesLocation(); | ||
|
||
public abstract Map<BuildStateEnum, String> getNormalTemplateMap(); | ||
public abstract Map<BuildStateEnum, String> getBranchTemplateMap(); | ||
|
||
public AbstractFileSetBasedWebHookTemplate(WebHookTemplateManager manager) { | ||
setTemplateManager(manager); | ||
} | ||
|
||
@Override | ||
public void register() { | ||
templateContent.clear(); | ||
branchTemplateContent.clear(); | ||
loadTemplatesFromFileSet(); | ||
if (!templateContent.isEmpty() && !branchTemplateContent.isEmpty()){ | ||
super.register(this); | ||
} else { | ||
if (templateContent.isEmpty()){ | ||
Loggers.SERVER.error(getLoggingName() + " :: Failed to register template " + getTemplateShortName() + ". No regular template configurations were found."); | ||
} | ||
if (branchTemplateContent.isEmpty()){ | ||
Loggers.SERVER.error(getLoggingName() + " :: Failed to register template " + getTemplateShortName() + ". No branch template configurations were found."); | ||
} | ||
} | ||
} | ||
|
||
private URL findPropertiesFileUrlInVariousClassloaders(String propertiesFile) { | ||
final ClassLoader[] classLoaders = {AbstractFileSetBasedWebHookTemplate.class.getClassLoader(), ClassLoader.getSystemClassLoader()}; | ||
URL url = null; | ||
for (ClassLoader cl : classLoaders){ | ||
if (cl != null){ | ||
url = cl.getResource(propertiesFile); | ||
if (url != null){ | ||
break; | ||
} | ||
} | ||
} | ||
return url; | ||
} | ||
|
||
private static final int BUFFER_SIZE = 4 * 1024; | ||
|
||
public static String inputStreamToString(InputStream inputStream, String charsetName) | ||
throws IOException { | ||
StringBuilder builder = new StringBuilder(); | ||
InputStreamReader reader = new InputStreamReader(inputStream, charsetName); | ||
char[] buffer = new char[BUFFER_SIZE]; | ||
int length; | ||
while ((length = reader.read(buffer)) != -1) { | ||
builder.append(buffer, 0, length); | ||
} | ||
return builder.toString(); | ||
} | ||
|
||
public String readFully(InputStream inputStream, String encoding) | ||
throws IOException { | ||
return new String(readFully(inputStream), encoding); | ||
} | ||
|
||
private byte[] readFully(InputStream inputStream) | ||
throws IOException { | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
byte[] buffer = new byte[1024]; | ||
int length = 0; | ||
while ((length = inputStream.read(buffer)) != -1) { | ||
baos.write(buffer, 0, length); | ||
} | ||
return baos.toByteArray(); | ||
} | ||
|
||
private String readFileContents(String templateFile){ | ||
URL url = findPropertiesFileUrlInVariousClassloaders(templateFile); | ||
if (url != null) { | ||
InputStream in = null; | ||
try { | ||
in = url.openStream(); | ||
return inputStreamToString(in, "UTF-8"); | ||
} catch (IOException e) { | ||
Loggers.SERVER.error(getLoggingName() + " :: An Error occurred trying to load the template file: " + templateFile + "."); | ||
Loggers.SERVER.debug(e); | ||
|
||
} finally { | ||
try { | ||
if (in != null){ | ||
in.close(); | ||
} | ||
} catch (IOException e) { | ||
Loggers.SERVER.error(e); | ||
} | ||
} | ||
} else { | ||
Loggers.SERVER.error(getLoggingName() + " :: An Error occurred trying to load the template file: " + templateFile + ". The file was not found in the classpath."); | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* Load the template from a file, rather than doing silly string escaping in java. | ||
*/ | ||
private void loadTemplatesFromFileSet() { | ||
for (BuildStateEnum state : BuildStateEnum.getNotifyStates()){ | ||
if (getNormalTemplateMap().containsKey(state)){ | ||
String templateContents = readFileContents(getTemplateFilesLocation() + File.separator + getNormalTemplateMap().get(state)); | ||
if (templateContents != null) { | ||
templateContent.put(state, WebHookTemplateContent.create( | ||
state.getShortName(), | ||
templateContents, | ||
true, | ||
this.getPreferredDateTimeFormat())); | ||
Loggers.SERVER.info(getLoggingName() + " :: Found and loaded normal template for: " + state.getShortName()); | ||
Loggers.SERVER.debug(getLoggingName() + " :: Template content is: " + templateContents); | ||
|
||
} | ||
} | ||
} | ||
for (BuildStateEnum state : BuildStateEnum.getNotifyStates()){ | ||
if (getBranchTemplateMap().containsKey(state)){ | ||
String templateContents = readFileContents(getTemplateFilesLocation() + File.separator + getBranchTemplateMap().get(state)); | ||
if (templateContents != null) { | ||
branchTemplateContent.put(state, WebHookTemplateContent.create( | ||
state.getShortName(), | ||
templateContents, | ||
true, | ||
this.getPreferredDateTimeFormat())); | ||
Loggers.SERVER.info(getLoggingName() + " :: Found and loaded branch template for: " + state.getShortName()); | ||
Loggers.SERVER.debug(getLoggingName() + " :: Template content is: " + templateContents); | ||
|
||
} | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public WebHookTemplateContent getTemplateForState(BuildStateEnum buildState) { | ||
if (templateContent.containsKey(buildState)){ | ||
return (templateContent.get(buildState)).copy(); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public WebHookTemplateContent getBranchTemplateForState(BuildStateEnum buildState) { | ||
if (branchTemplateContent.containsKey(buildState)){ | ||
return (branchTemplateContent.get(buildState)).copy(); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Set<BuildStateEnum> getSupportedBuildStates() { | ||
return templateContent.keySet(); | ||
} | ||
|
||
@Override | ||
public Set<BuildStateEnum> getSupportedBranchBuildStates() { | ||
return branchTemplateContent.keySet(); | ||
} | ||
|
||
} |
81 changes: 81 additions & 0 deletions
81
tcwebhooks-core/src/main/java/webhook/teamcity/payload/template/FlowdockWebHookTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package webhook.teamcity.payload.template; | ||
|
||
import java.util.Map; | ||
import java.util.TreeMap; | ||
|
||
import webhook.teamcity.BuildStateEnum; | ||
import webhook.teamcity.payload.WebHookTemplateManager; | ||
import webhook.teamcity.payload.format.WebHookPayloadJsonTemplate; | ||
|
||
public class FlowdockWebHookTemplate extends AbstractFileSetBasedWebHookTemplate { | ||
|
||
private Map<BuildStateEnum, String> normalTemplateMap = new TreeMap<BuildStateEnum, String>(); | ||
private Map<BuildStateEnum, String> branchTemplateMap = new TreeMap<BuildStateEnum, String>(); | ||
|
||
public FlowdockWebHookTemplate(WebHookTemplateManager manager) { | ||
super(manager); | ||
|
||
normalTemplateMap.put(BuildStateEnum.BUILD_BROKEN, "flowdock-buildBroken-buildFailed-buildInterrupted-normal.json"); | ||
normalTemplateMap.put(BuildStateEnum.BUILD_FAILED, "flowdock-buildBroken-buildFailed-buildInterrupted-normal.json"); | ||
normalTemplateMap.put(BuildStateEnum.BUILD_INTERRUPTED, "flowdock-buildBroken-buildFailed-buildInterrupted-normal.json"); | ||
normalTemplateMap.put(BuildStateEnum.BUILD_STARTED, "flowdock-buildStarted-normal.json"); | ||
normalTemplateMap.put(BuildStateEnum.BUILD_SUCCESSFUL, "flowdock-buildSuccessful-buildFixed-normal.json"); | ||
normalTemplateMap.put(BuildStateEnum.BUILD_FIXED, "flowdock-buildSuccessful-buildFixed-normal.json"); | ||
normalTemplateMap.put(BuildStateEnum.RESPONSIBILITY_CHANGED, "flowdock-responsibilityChanged-normal.json"); | ||
|
||
branchTemplateMap.put(BuildStateEnum.BUILD_BROKEN, "flowdock-buildBroken-buildFailed-buildInterrupted-branch.json"); | ||
branchTemplateMap.put(BuildStateEnum.BUILD_FAILED, "flowdock-buildBroken-buildFailed-buildInterrupted-branch.json"); | ||
branchTemplateMap.put(BuildStateEnum.BUILD_INTERRUPTED, "flowdock-buildBroken-buildFailed-buildInterrupted-branch.json"); | ||
branchTemplateMap.put(BuildStateEnum.BUILD_STARTED, "flowdock-buildStarted-branch.json"); | ||
branchTemplateMap.put(BuildStateEnum.BUILD_SUCCESSFUL, "flowdock-buildSuccessful-buildFixed-branch.json"); | ||
branchTemplateMap.put(BuildStateEnum.BUILD_FIXED, "flowdock-buildSuccessful-buildFixed-branch.json"); | ||
branchTemplateMap.put(BuildStateEnum.RESPONSIBILITY_CHANGED, "flowdock-responsibilityChanged-branch.json"); | ||
|
||
} | ||
|
||
@Override | ||
public String getTemplateDescription() { | ||
return "Flowdock JSON templates"; | ||
} | ||
|
||
@Override | ||
public String getTemplateToolTipText() { | ||
return "Supports the TeamCity Flowdock JSON integration"; | ||
} | ||
|
||
@Override | ||
public String getTemplateShortName() { | ||
return "flowdock"; | ||
} | ||
|
||
@Override | ||
public boolean supportsPayloadFormat(String payloadFormat) { | ||
return payloadFormat.equalsIgnoreCase(WebHookPayloadJsonTemplate.FORMAT_SHORT_NAME); | ||
} | ||
|
||
@Override | ||
public String getPreferredDateTimeFormat() { | ||
return ""; | ||
} | ||
|
||
@Override | ||
public String getLoggingName() { | ||
return this.getClass().getSimpleName(); | ||
} | ||
|
||
@Override | ||
public String getTemplateFilesLocation() { | ||
return "webhook/teamcity/payload/template/flowdock"; | ||
} | ||
|
||
@Override | ||
public Map<BuildStateEnum, String> getNormalTemplateMap() { | ||
return this.normalTemplateMap; | ||
} | ||
|
||
@Override | ||
public Map<BuildStateEnum, String> getBranchTemplateMap() { | ||
return this.branchTemplateMap; | ||
} | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
tcwebhooks-core/src/main/java/webhook/teamcity/payload/util/StringSanitiser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package webhook.teamcity.payload.util; | ||
|
||
public class StringSanitiser { | ||
|
||
public static String sanitise(String dirtyString) { | ||
return dirtyString | ||
.replace("<", "_") | ||
.replace(">", "_") | ||
.replace("\\", "_") | ||
.replace("/", "_") | ||
.replace("$", "_") | ||
.replace("%", "_") | ||
.replace("#", "_") | ||
.replace("@", "_") | ||
.replace("!", "_") | ||
.replace("`", "_") | ||
.replace("~", "_") | ||
.replace("?", "_") | ||
.replace("|", "_") | ||
.replace("*", "_") | ||
.replace("(", "_") | ||
.replace(")", "_") | ||
.replace("^", "_") | ||
; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
...y/payload/template/flowdock/flowdock-buildBroken-buildFailed-buildInterrupted-branch.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"event": "activity", | ||
"author": { | ||
"name": "TeamCity", | ||
"avatar": "https://raw.githubusercontent.com/tcplugins/tcWebHooks/custom-templates/tcwebhooks-core/src/docs/images/buildFailed%402x.png" | ||
}, | ||
"title": "updated build status to ${notifyType}", | ||
"external_thread_id": "teamcity-${buildName}-${sanitize(branchName)}", | ||
"tags": [ "#${buildExternalTypeId}", "#${projectExternalId}", "#${notifyType}", "#${branchDisplayName}", "#teamcity" ], | ||
"thread": { | ||
"title": "${buildName} (${sanitize(branchDisplayName)})", | ||
"fields": [ | ||
{ "label": "Build", "value": "${buildFullName}" }, | ||
{ "label": "Branch", "value": "${branchDisplayName}" }, | ||
{ "label": "Default Branch", "value": "${branchIsDefault}" }, | ||
{ "label": "Triggered By", "value": "${triggeredBy}" }, | ||
{ "label": "Agent", "value": "${agentName}" } | ||
], | ||
"body": "<a href=\"${rootUrl}/project.html?projectId=${projectId}\">${projectName}</a> :: <a href=\"${rootUrl}/viewType.html?buildTypeId=${buildTypeId}\">${buildName}</a> # <a href=\"${rootUrl}/viewLog.html?buildTypeId=${buildTypeId}&buildId=${buildId}\"><strong>${buildNumber}</strong></a> has <strong>${buildStateDescription}</strong> with a status of <a href=\"${rootUrl}/viewLog.html?buildTypeId=${buildTypeId}&buildId=${buildId}\"><strong>${buildResult}</strong></a>", | ||
"external_url": "${buildStatusUrl}", | ||
"status": { | ||
"color": "red", | ||
"value": "${buildResult}" | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...y/payload/template/flowdock/flowdock-buildBroken-buildFailed-buildInterrupted-normal.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"event": "activity", | ||
"author": { | ||
"name": "TeamCity", | ||
"avatar": "https://raw.githubusercontent.com/tcplugins/tcWebHooks/custom-templates/tcwebhooks-core/src/docs/images/buildFailed%402x.png" | ||
}, | ||
"title": "updated build status to ${notifyType}", | ||
"external_thread_id": "teamcity-${buildName}", | ||
"tags": [ "#${buildExternalTypeId}", "#${projectExternalId}", "#${notifyType}", "#teamcity" ], | ||
"thread": { | ||
"title": "${buildName}", | ||
"fields": [ | ||
{ "label": "Build", "value": "${buildFullName}" }, | ||
{ "label": "Triggered By", "value": "${triggeredBy}" }, | ||
{ "label": "Agent", "value": "${agentName}" } | ||
], | ||
"body": "<a href=\"${rootUrl}/project.html?projectId=${projectId}\">${projectName}</a> :: <a href=\"${rootUrl}/viewType.html?buildTypeId=${buildTypeId}\">${buildName}</a> # <a href=\"${rootUrl}/viewLog.html?buildTypeId=${buildTypeId}&buildId=${buildId}\"><strong>${buildNumber}</strong></a> has <strong>${buildStateDescription}</strong> with a status of <a href=\"${rootUrl}/viewLog.html?buildTypeId=${buildTypeId}&buildId=${buildId}\"><strong>${buildResult}</strong></a>", | ||
"external_url": "${buildStatusUrl}", | ||
"status": { | ||
"color": "red", | ||
"value": "${buildResult}" | ||
} | ||
} | ||
} |
Oops, something went wrong.