Skip to content
This repository was archived by the owner on Feb 13, 2020. It is now read-only.

Commit

Permalink
Add action to receive bot webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
ikikko committed Jan 2, 2015
1 parent de5378b commit ec16ec6
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.jenkinsci.plugins.typetalk;

import hudson.Extension;
import hudson.model.RootAction;
import org.jenkinsci.plugins.typetalk.webhookaction.WebhookExecutor;
import org.jenkinsci.plugins.typetalk.webhookaction.WebhookExecutorFactory;
import org.jenkinsci.plugins.typetalk.webhookaction.WebhookParser;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;

import javax.servlet.ServletException;
import java.io.IOException;

@Extension
public class TypetalkWebhookAction implements RootAction {

@Override
public String getIconFileName() {
return null;
}

@Override
public String getDisplayName() {
return null;
}

@Override
public String getUrlName() {
return "typetalk";
}

@RequirePOST
public HttpResponse doNotify(final StaplerRequest request) {

return new HttpResponse() {
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
throws IOException, ServletException {

String message = new WebhookParser(req).parse();
WebhookExecutor executor = WebhookExecutorFactory.create(req, rsp, message);
executor.execute();

// for (ResponseContributor c : contributors) {
// c.addHeaders(req, rsp);
// }
// PrintWriter w = rsp.getWriter();
// for (ResponseContributor c : contributors) {
// c.writeBody(req, rsp, w);
// }
}
};
}

static class TypetalkRequest {
Object topic;

// {"topic":{"id":9526,"name":"Typetalk Hack Tokyo Dec 2014","suggestion":"Typetalk Hack Tokyo Dec 2014","lastPostedAt":"2014-12-16T11:17:44Z","createdAt":"2014-12-16T08:32:53Z","updatedAt":"2014-12-16T08:32:53Z"},"post":{"id":754399,"topicId":9526,"replyTo":null,"message":"@ikikkobot+ hello","account":{"id":10,"name":"ikikko","fullName":"nakamura","suggestion":"nakamura","imageUrl":"https://typetalk.in/accounts/10/profile_image.png?t=1413099125640","createdAt":"2012-03-07T05:13:52Z","updatedAt":"2014-12-16T09:12:55Z"},"mention":null,"attachments":[],"likes":[],"talks":[],"links":[],"createdAt":"2014-12-16T11:17:44Z","updatedAt":"2014-12-16T11:17:44Z"}}

// @see https://github.com/github/hubot-scripts/blob/master/src/scripts/jenkins.coffee
//
// # Commands:
// # hubot jenkins b <jobNumber> - builds the job specified by jobNumber. List jobs to get number.
// # hubot jenkins build <job> - builds the specified Jenkins job
// # hubot jenkins build <job>, <params> - builds the specified Jenkins job with parameters as key=value&key2=value2
// # hubot jenkins list <filter> - lists Jenkins jobs
// # hubot jenkins describe <job> - Describes the specified Jenkins job
// # hubot jenkins last <job> - Details about the last build for the specified Jenkins job

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.jenkinsci.plugins.typetalk.webhookaction;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public abstract class WebhookExecutor {
protected HttpServletRequest req;
protected HttpServletResponse rsp;
protected String command;
protected PrintWriter writer;

protected WebhookExecutor(HttpServletRequest req, HttpServletResponse rsp, String command) {
this.req = req;
this.rsp = rsp;
this.command = command;

rsp.setContentType("text/plain");

try {
writer = rsp.getWriter();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

public abstract void execute();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.jenkinsci.plugins.typetalk.webhookaction;

import org.jenkinsci.plugins.typetalk.webhookaction.executorimpl.BuildExecutor;
import org.jenkinsci.plugins.typetalk.webhookaction.executorimpl.UndefinedExecutor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WebhookExecutorFactory {
public static WebhookExecutor create(HttpServletRequest req, HttpServletResponse rsp, String message) {
String[] splitMessage = message.split("\\s");
String command = splitMessage[1];

switch (command) {
case "build":
String job = splitMessage[2];
return new BuildExecutor(req, rsp, job);
default:
return new UndefinedExecutor(req, rsp, command);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.jenkinsci.plugins.typetalk.webhookaction;

import net.sf.json.JSONObject;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;

public class WebhookParser {

private HttpServletRequest req;

public WebhookParser(HttpServletRequest req) {
this.req = req;
}

public String parse() {
try {
StringBuilder sb = new StringBuilder();
BufferedReader reader = req.getReader();

String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
}

JSONObject jsonObject = JSONObject.fromObject(sb.toString());
return jsonObject.getJSONObject("post").getString("message");

} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.jenkinsci.plugins.typetalk.webhookaction.executorimpl;

import hudson.model.AbstractProject;
import hudson.model.Cause;
import hudson.model.TopLevelItem;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.typetalk.webhookaction.WebhookExecutor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.logging.Logger;

public class BuildExecutor extends WebhookExecutor {

// TODO nakamura : テスト追加
// TODO nakamura : パラメータ付きビルドの対応
// TODO nakamura : 認証付きの場合の確認

private static final Logger LOGGER = Logger.getLogger(BuildExecutor.class.getName());

private String job;

public BuildExecutor(HttpServletRequest req, HttpServletResponse rsp, String job) {
super(req, rsp, "build");
this.job = job;
}

@Override
public void execute() {
TopLevelItem item = Jenkins.getInstance().getItemMap().get(job);
if (item == null || !(item instanceof AbstractProject)) {
String message = "'" + job + "' is not found";
LOGGER.warning(message);

rsp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
writer.println(message);

return;
}

Cause.RemoteCause cause = new Cause.RemoteCause(req.getRemoteAddr(), "by Typetalk Webhook");
((AbstractProject) item).scheduleBuild(cause);

String message = "'" + job + "' has been scheduled";
LOGGER.info(message);

rsp.setStatus(HttpServletResponse.SC_OK);
writer.println(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.jenkinsci.plugins.typetalk.webhookaction.executorimpl;

import org.jenkinsci.plugins.typetalk.webhookaction.WebhookExecutor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.logging.Logger;

public class UndefinedExecutor extends WebhookExecutor {

private static final Logger LOGGER = Logger.getLogger(UndefinedExecutor.class.getName());

public UndefinedExecutor(HttpServletRequest req, HttpServletResponse rsp, String command) {
super(req, rsp, command);
}

@Override
public void execute() {
String message = "command '" + command + "' is not defined";
LOGGER.warning(message);

rsp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
writer.println(message);
}
}

0 comments on commit ec16ec6

Please sign in to comment.