Project: fast-http
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); 
    } 
 
 
}