From 96c5c9fef2277a983b5667a4bc8fa01ee5a2c90d Mon Sep 17 00:00:00 2001 From: ikikko Date: Sun, 25 Jan 2015 02:33:44 +0900 Subject: [PATCH] Handle response back --- .../typetalk/TypetalkWebhookAction.java | 1 - .../plugins/typetalk/api/TypetalkMessage.java | 21 +++++++++++++++ .../webhookaction/WebhookExecutor.java | 27 +++++++++++++++---- .../executorimpl/BuildExecutor.java | 6 ++--- .../executorimpl/UndefinedExecutor.java | 2 +- .../executorimpl/BuildExecutorSpec.groovy | 16 ++++++++++- 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/typetalk/TypetalkWebhookAction.java b/src/main/java/org/jenkinsci/plugins/typetalk/TypetalkWebhookAction.java index 3754568..4ac7731 100644 --- a/src/main/java/org/jenkinsci/plugins/typetalk/TypetalkWebhookAction.java +++ b/src/main/java/org/jenkinsci/plugins/typetalk/TypetalkWebhookAction.java @@ -20,7 +20,6 @@ @Extension public class TypetalkWebhookAction implements RootAction { - // TODO handle response back ( if Typetalk supports it ) // TODO enable alias job name to simplify build parameter @Override diff --git a/src/main/java/org/jenkinsci/plugins/typetalk/api/TypetalkMessage.java b/src/main/java/org/jenkinsci/plugins/typetalk/api/TypetalkMessage.java index 085e9ad..076c149 100644 --- a/src/main/java/org/jenkinsci/plugins/typetalk/api/TypetalkMessage.java +++ b/src/main/java/org/jenkinsci/plugins/typetalk/api/TypetalkMessage.java @@ -1,6 +1,7 @@ package org.jenkinsci.plugins.typetalk.api; import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; import hudson.model.Result; import jenkins.model.Jenkins; import org.apache.commons.lang.StringUtils; @@ -87,4 +88,24 @@ public String messageWithBuildInfo(AbstractBuild build) { return builder.toString(); } + public String messageWithProjectInfo(AbstractProject project) { + final String rootUrl = Jenkins.getInstance().getRootUrl(); + if (StringUtils.isEmpty(rootUrl)) { + throw new IllegalStateException("Root URL isn't configured yet. Cannot compute absolute URL."); + } + + final StringBuilder builder = new StringBuilder(); + builder.append(emoji.symbol); + builder.append(" "); + builder.append(message); + builder.append("\n"); + builder.append("\n"); + builder.append(rootUrl); + if (project != null) { + builder.append(project.getUrl()); + } + + return builder.toString(); + } + } diff --git a/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/WebhookExecutor.java b/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/WebhookExecutor.java index ca9c239..38e254d 100644 --- a/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/WebhookExecutor.java +++ b/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/WebhookExecutor.java @@ -1,5 +1,8 @@ package org.jenkinsci.plugins.typetalk.webhookaction; +import hudson.model.AbstractProject; +import net.sf.json.JSONObject; +import org.jenkinsci.plugins.typetalk.api.TypetalkMessage; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.http.HttpServletResponse; @@ -24,7 +27,11 @@ protected WebhookExecutor(WebhookRequest req, StaplerResponse rsp, String comman public abstract void execute(); protected void output(String message) { - outputInternal(Level.INFO, message, HttpServletResponse.SC_OK); + output(message, null); + } + + protected void output(String message, AbstractProject project) { + outputInternal(Level.INFO, HttpServletResponse.SC_OK, TypetalkMessage.Emoji.SMILEY, message, project); } protected void outputError(String message) { @@ -32,18 +39,28 @@ protected void outputError(String message) { } protected void outputError(String message, int status) { - outputInternal(Level.WARNING, message, status); + outputInternal(Level.WARNING, status, TypetalkMessage.Emoji.CRY, message, null); } - private void outputInternal(Level level, String message, int status) { + private void outputInternal(Level level, int status, TypetalkMessage.Emoji emoji, String message, AbstractProject project) { try { logger.log(level, message); - rsp.setContentType("text/plain"); + rsp.setContentType("application/json"); rsp.setStatus(status); - rsp.getWriter().println(message); + rsp.getWriter().println(buildResponseMessage(emoji, message, project)); } catch (IOException e) { throw new IllegalStateException(e); } } + + private String buildResponseMessage(TypetalkMessage.Emoji emoji, String message, AbstractProject project) { + JSONObject jsonObject = new JSONObject(); + + TypetalkMessage typetalkMessage = new TypetalkMessage(emoji, message); + jsonObject.element("message", typetalkMessage.messageWithProjectInfo(project)); + jsonObject.element("replyTo", req.getPostId()); + + return jsonObject.toString(); + } } diff --git a/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutor.java b/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutor.java index c758236..f625afc 100644 --- a/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutor.java +++ b/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutor.java @@ -47,19 +47,19 @@ private void parseParameters() { public void execute() { TopLevelItem item = Jenkins.getInstance().getItemMap().get(job); if (item == null || !(item instanceof AbstractProject)) { - outputError("'" + job + "' is not found"); + outputError("Project [ " + job + " ] is not found"); return; } AbstractProject project = ((AbstractProject) item); if (!project.hasPermission(Item.BUILD)) { String name = Jenkins.getAuthentication().getName(); - outputError(String.format("'%s' cannot be built by '%s'", job, name), HttpServletResponse.SC_FORBIDDEN); + outputError(String.format("Project [ %s ] cannot be built by '%s'", job, name), HttpServletResponse.SC_FORBIDDEN); return; } Jenkins.getInstance().getQueue().schedule(project, project.getQuietPeriod(), getParametersAction(project), getCauseAction()); - output("'" + job + "' has been scheduled"); + output("Project [ " + job + " ] has been scheduled", project); } private Action getParametersAction(AbstractProject project) { diff --git a/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/UndefinedExecutor.java b/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/UndefinedExecutor.java index 5c2615e..62bb8af 100644 --- a/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/UndefinedExecutor.java +++ b/src/main/java/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/UndefinedExecutor.java @@ -12,6 +12,6 @@ public UndefinedExecutor(WebhookRequest req, StaplerResponse rsp, String command @Override public void execute() { - outputError("command '" + command + "' is not defined"); + outputError("Command [ " + command + " ] is not defined"); } } diff --git a/src/test/groovy/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutorSpec.groovy b/src/test/groovy/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutorSpec.groovy index cc208cf..993d99f 100644 --- a/src/test/groovy/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutorSpec.groovy +++ b/src/test/groovy/org/jenkinsci/plugins/typetalk/webhookaction/executorimpl/BuildExecutorSpec.groovy @@ -6,6 +6,7 @@ import hudson.security.ACL import hudson.security.AuthorizationStrategy import hudson.security.Permission import hudson.util.OneShotEvent +import jenkins.model.JenkinsLocationConfiguration import org.acegisecurity.Authentication import org.acegisecurity.context.SecurityContextHolder import org.acegisecurity.providers.TestingAuthenticationToken @@ -22,7 +23,7 @@ import javax.servlet.http.HttpServletResponse class BuildExecutorSpec extends Specification { - @Rule JenkinsRule j = new JenkinsRule() + @Rule JenkinsRule j def req = Mock(WebhookRequest) def res = Mock(StaplerResponse) @@ -38,6 +39,7 @@ class BuildExecutorSpec extends Specification { def "execute : project is not found"() { setup: + setUpRootUrl() executor = new BuildExecutor(req, res, "typetalk-plugin", []) when: @@ -49,6 +51,7 @@ class BuildExecutorSpec extends Specification { def "execute : no parameter"() { setup: + setUpRootUrl() setUpProject([]) executor = new BuildExecutor(req, res, "typetalk-plugin", []) @@ -61,6 +64,7 @@ class BuildExecutorSpec extends Specification { def "execute : parameter without key"() { setup: + setUpRootUrl() setUpProject([ new StringParameterDefinition("version", null, null) ]) @@ -77,6 +81,7 @@ class BuildExecutorSpec extends Specification { def "execute : single parameter"() { setup: + setUpRootUrl() setUpProject([ new StringParameterDefinition("version", null, null) ]) @@ -93,6 +98,7 @@ class BuildExecutorSpec extends Specification { def "execute : multiple parameters"() { setup: + setUpRootUrl() setUpProject([ new StringParameterDefinition("version", null, null), new StringParameterDefinition("env", null, null) @@ -111,6 +117,7 @@ class BuildExecutorSpec extends Specification { def "execute : default parameter"() { setup: + setUpRootUrl() setUpProject([ new StringParameterDefinition("version", "1.0.0-SNAPSHOT", null) ]) @@ -127,6 +134,7 @@ class BuildExecutorSpec extends Specification { def "execute : illegal parameter format"() { setup: + setUpRootUrl() setUpProject([ new StringParameterDefinition("version", null, null), new StringParameterDefinition("env", null, null) @@ -145,6 +153,7 @@ class BuildExecutorSpec extends Specification { def "execute : not set without key when multiple parameters are defined"() { setup: + setUpRootUrl() setUpProject([ new StringParameterDefinition("version", null, null), new StringParameterDefinition("env", null, null) @@ -164,6 +173,7 @@ class BuildExecutorSpec extends Specification { @Unroll def "execute : #username"() { setup: + setUpRootUrl() setUpProject([]) executor = new BuildExecutor(req, res, "typetalk-plugin", []) @@ -187,6 +197,10 @@ class BuildExecutorSpec extends Specification { // --- helper method --- + def setUpRootUrl() { + JenkinsLocationConfiguration.get().url = "http://localhost:8080/" + } + def setUpProject(spds) { project = j.createFreeStyleProject("typetalk-plugin")