Created
November 29, 2012 15:49
-
-
Save kmila/4169933 to your computer and use it in GitHub Desktop.
CRUCIBLE REST API
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
import static java.lang.Math.min; | |
import static java.lang.String.format; | |
import static java.lang.System.getProperty; | |
import static java.nio.charset.Charset.defaultCharset; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.util.Arrays; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.ExecutionException; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.ThreadPoolExecutor; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.TimeoutException; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
import javax.xml.parsers.ParserConfigurationException; | |
import javax.xml.xpath.XPathConstants; | |
import javax.xml.xpath.XPathExpressionException; | |
import javax.xml.xpath.XPathFactory; | |
import org.w3c.dom.Document; | |
import org.w3c.dom.NodeList; | |
import org.xml.sax.SAXException; | |
import com.google.common.base.Throwables; | |
import com.google.common.io.CharStreams; | |
import com.google.common.util.concurrent.ThreadFactoryBuilder; | |
public class Main { | |
/** | |
* @param args Username and password. Use eclipse's variable ${password_prompt}. | |
*/ | |
public static void main(String[] args) throws Exception { | |
Crucible.restV1(args[0], args[1]).maxRequests(1).run(); | |
} | |
} | |
enum Cmd { | |
CREATE("PUT"), | |
READ("GET"), | |
UPDATE("POST"), | |
DELETE("DELETE"); | |
public final String cmd; | |
private Cmd(String cmd) { | |
this.cmd = cmd; | |
} | |
} | |
class Crucible implements Runnable { | |
private static final String REST_SERVICE_URL = "http://crucible/rest-service"; | |
// Crucible REST API | |
private static final String AUTH_URI = REST_SERVICE_URL + "/auth-v1/login?userName=%s&password=%s"; | |
private static final String REVIEW_URI = REST_SERVICE_URL + "/reviews-v1"; | |
private static final String GET_REVIEW = REVIEW_URI + "/filter/toReview?FEAUTH=%s"; // TODO by date? | |
private static final String POST_COMPLETE = REVIEW_URI + "/%s/complete?FEAUTH=%s"; | |
private final String user; | |
private final String pass; | |
private int maxRequests = 100; | |
private Crucible(String user, String pass) { | |
this.user = user; | |
this.pass = pass; | |
} | |
public static Crucible restV1(String user, String pass) throws Exception { | |
return new Crucible(user, pass); | |
} | |
/** | |
* Calls crucible REST API, should run asynchronously. | |
*/ | |
public void run() { | |
String authToken = getAuthData(); | |
if (authToken == null) { | |
throw new IllegalArgumentException("Invalid auth for " + user); | |
} | |
String[] cruIds = getPendingReviews(authToken); | |
if (cruIds.length == 0 || cruIds[0] == null) { | |
throw new UnsupportedOperationException("To review: not found."); | |
} | |
completeReviews(authToken, cruIds); | |
} | |
/** | |
* @return No reviews found for filter "To Review" | |
*/ | |
private String completeReviews(String authToken, String... ids) { | |
StringBuilder result = new StringBuilder(); | |
for (String id : ids) { | |
result.append(curl.httpRequest(Cmd.UPDATE, format(POST_COMPLETE, id, authToken))); | |
} | |
return result.toString(); | |
} | |
private String[] getPendingReviews(String authToken) { | |
try { | |
return parseXml("id", maxRequests, GET_REVIEW, authToken); | |
} | |
catch (Exception e) { | |
throw Throwables.propagate(e); | |
} | |
} | |
private String getAuthData() { | |
try { | |
return parseXml("token", 1, AUTH_URI, user, pass)[0]; | |
} | |
catch (Exception e) { | |
throw Throwables.propagate(e); | |
} | |
} | |
private static String[] parseXml(String tag, int maxRequests, String uri, Object... args) throws Exception { | |
Document xml = getXml(format(uri, args)); | |
String[] requests = parseXauth(xml, tag, maxRequests); | |
System.out.println(format("tag=%s, uri=%s, data=%s, charset=%s", tag, format(uri, args), Arrays.toString(requests), xml.getInputEncoding())); | |
return requests; | |
} | |
private static String[] parseXauth(Document xml, String tag, int maxRequests) throws XPathExpressionException { | |
Object result = XPathFactory.newInstance().newXPath().evaluate(format("//%s/text()", tag), xml, XPathConstants.NODESET); | |
NodeList nodes = (NodeList) result; | |
String[] ids = new String[min(nodes.getLength(), maxRequests)]; | |
for (int i = 0; i < ids.length; i++) { | |
String nodeValue = nodes.item(i).getNodeValue(); | |
if (nodeValue == null) { | |
throw new IllegalStateException("No text for node " + tag); | |
} | |
ids[i] = nodeValue; | |
} | |
return ids; | |
} | |
private static Document getXml(String uri) throws ParserConfigurationException, SAXException, IOException { | |
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); | |
domFactory.setNamespaceAware(true); | |
return domFactory.newDocumentBuilder().parse(uri); | |
} | |
public Crucible maxRequests(int maxRequests) { | |
this.maxRequests = maxRequests; | |
return this; | |
} | |
} | |
class curl { | |
public static final String CURL_CMD = getProperty("curl", "curl"); | |
private static final long TIMEOUT = 5; | |
private static final ThreadPoolExecutor EXECUTOR_SERVICE = (ThreadPoolExecutor) Executors.newFixedThreadPool(1, new ThreadFactoryBuilder() | |
.setNameFormat("CMD-%d").build()); | |
public static String httpRequest(Cmd cmd, String uri) { | |
try { | |
return exec(CURL_CMD, Boolean.getBoolean("noop") ? "--help" : "-v", "-X", cmd.cmd, uri); | |
} | |
catch (Exception e) { | |
throw Throwables.propagate(e); | |
} | |
} | |
public static String exec(String... cmd) throws Exception { | |
System.out.println("Running " + Arrays.toString(cmd)); | |
Process p = new ProcessBuilder().redirectErrorStream(true).command(cmd).start(); | |
return parseResponse(p, new InputStreamReader(p.getInputStream(), defaultCharset())); | |
} | |
private static String parseResponse(final Process p, final InputStreamReader response) throws InterruptedException, ExecutionException, | |
TimeoutException, IOException { | |
try { | |
String result = EXECUTOR_SERVICE.submit(new Callable<String>() { | |
public String call() throws Exception { | |
String result = CharStreams.toString(new BufferedReader(response)); | |
p.waitFor(); | |
return result; | |
} | |
}).get(TIMEOUT, TimeUnit.SECONDS); | |
System.out.println("Process exited with status " + p.exitValue()); | |
return result; | |
} | |
finally { | |
response.close(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment