package org.neo4j.smack.test.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.URI; import java.util.Map; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.map.ObjectMapper; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; /** * @author mh * @since 12.12.11 */ public class REST { private final static JsonFactory jsonFactory = new JsonFactory(new ObjectMapper()); private final static Client client = Client.create(); private String baseUri; private GraphDatabaseService gds; public class Request { private final String path; private GraphDatabaseService gds; private WebResource.Builder resource; private int status; private URI location; private Object entity; public Request(String path, String baseUri, GraphDatabaseService gds) { this.path = path; this.gds = gds; resource = client .resource(mergeUri(baseUri, path)) .accept(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON); } public Request get() { return handle("GET", resource.get(ClientResponse.class)); } public Request post(Object data) { return handle("POST", resource.post(ClientResponse.class, formatJson(data))); } public Request post() { return handle("POST", resource.post(ClientResponse.class)); } public Request put() { return handle("PUT", resource.put(ClientResponse.class)); } public Request put(Object data) { return handle("PUT", resource.put(ClientResponse.class, formatJson(data))); } public Request delete() { return handle("DELETE", resource.delete(ClientResponse.class)); } private Request handle(String method, ClientResponse response) { status = response.getStatus(); location = response.getLocation(); readEntityIfPossible(response); response.close(); //System.out.printf(method + " from [%s], status code [%d] location: %s, returned data: %n%s\n", path, status, location, entity); return this; } private void readEntityIfPossible(ClientResponse response) { if (status != Response.Status.NO_CONTENT.getStatusCode() && response.getLength() > 0) { this.entity = readEntity(response, Object.class); } } private <T> T readEntity(ClientResponse response, Class<T> type) { try { final InputStream is = response.getEntityInputStream(); return jsonFactory.createJsonParser(new FilterInputStream(is) { @Override public int read() throws IOException { final int result = super.read(); System.out.print(Character.valueOf((char) result)); return result; } }).readValueAs(type); } catch (Exception e) { System.out.flush(); throw new RuntimeException("Error reading response object "+this, e); } } @Override public String toString() { return "Request{" + "status=" + status + ", location=" + location + ", entity=" + entity + ", path='" + path + '\'' + '}'; } private String formatJson(Object data) { try { final StringWriter result = new StringWriter(); jsonFactory.createJsonGenerator(result).writeObject(data); return result.toString(); } catch (IOException e) { throw new RuntimeException("error formatting json", e); } } public Request created() { return assertStatus(Response.Status.CREATED); } public Request ok() { return assertStatus(Response.Status.OK); } public Request location(String expected) { final String value = this.location.toString(); assertTrue("Location " + expected + " matches " + value, value.matches(expected)); return this; } public String location() { return this.location.toString(); } public Request expect(Object value) { assertEquals("Result ", value, this.entity); return this; } public Request expect(String path, Object value) { assertEquals("Result[" + path + "]", value, getPath(path)); return this; } public Request expectUri(String path, Object value) { assertEquals("Result[" + path + "]", mergeUri(baseUri, value.toString()), getPath(path)); return this; } private Object getPath(String path) { try { final String[] parts = path.split("[./]"); Map map = (Map) entity; for (int i = 0; i < parts.length - 1; i++) { String part = parts[i]; map = (Map) map.get(part); } return map.get(parts[parts.length - 1]); } catch(NullPointerException e) { throw new RuntimeException("Expected response to the following path of keys: " + path); } } public Request assertStatus(Response.Status expectedStatus) { assertEquals("status code is not " + expectedStatus, expectedStatus.getStatusCode(), status); return this; } public void compareNodeProperties(String... props) { checkPropertyContainer(node(), props); } public void compareRelationshipProperties(String... props) { checkPropertyContainer(relationship(), props); } private void checkPropertyContainer(PropertyContainer container, String[] props) { Map data = data(); for (String prop : props) { checkProperty(container, prop, data.get(prop)); } } private void checkProperty(PropertyContainer container, String prop, Object value) { if (prop.startsWith("!")) { assertFalse(container+" has not "+prop,container.hasProperty(prop.substring(1))); return; } assertEquals(container+" "+prop+": ",container.getProperty(prop), value); } public void checkRelationship(String...props) { checkPropertyContainer(relationship(), props); } private Map data() { return (Map) ((Map) entity).get("data"); } private Node node() { return gds.getNodeById(id()); } private Long id() { if (location!=null) { return extractId(location.toString()); } return extractId(selfUri()); } private Relationship relationship() { return gds.getRelationshipById(id()); } private String selfUri() { return (String) ((Map)entity).get("self"); } private Long extractId(String uri) { final String[] parts = uri.split("/"); return Long.valueOf(parts[parts.length - 1]); } public Request notFound() { assertStatus(Response.Status.NOT_FOUND); return this; } public Request noContent() { assertStatus(Response.Status.NO_CONTENT); return this; } public Request checkNodeProperty(long id, String prop, Object value) { checkProperty(gds.getNodeById(id),prop,value); return this; } public Request checkRelationshipProperty(long id, String prop, Object value) { checkProperty(gds.getRelationshipById(id),prop,value); return this; } public Request checkRelationshipProperty(String relationship, String prop, String value) { checkRelationshipProperty(extractId(relationship), prop, value); return this; } } public REST(String baseUri, GraphDatabaseService gds) { this.baseUri = baseUri; this.gds = gds; } private static String mergeUri(String base, String path) { if(path.startsWith("/")) { path = path.substring(1); } return (base + "/" + path).replaceAll("([^:]/)/", "$1"); } public Request from(String path) { return new Request(path, baseUri, gds); } public Request to(String path) { return new Request(path, baseUri, gds); } }