diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8157a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +cobertura.ser +.classpath +.project +.settings +target/ +*.swp diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1c08350 --- /dev/null +++ b/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + com.github.notifo4j + notifo-client + jar + 1.0-SNAPSHOT + notifo4j + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + + org.apache.httpcomponents + httpclient + 4.0.1 + + + com.google.code.gson + gson + 1.4 + + + junit + junit + 4.8.1 + test + + + org.jmock + jmock-junit4 + 2.5.1 + test + + + diff --git a/src/main/java/com/notifo/client/NotifoClient.java b/src/main/java/com/notifo/client/NotifoClient.java new file mode 100644 index 0000000..b56498f --- /dev/null +++ b/src/main/java/com/notifo/client/NotifoClient.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2010 Sean P. Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +/** + * @author sscanlon + * + */ +public interface NotifoClient { + + /** + * + * @param messageBody + * @return + * @throws NotifoException + */ + NotifoResponse sendMessage(NotifoMessage message) throws NotifoException; + + /** + * Send a message to a particular user + */ + NotifoResponse sendMessage(String to, String messageBody) throws NotifoException; + + /** + * Send a message to the default user (you) + */ + NotifoResponse sendMessage(String messageBody) throws NotifoException; + + /** + * + * @param userName + * @return + * @throws NotifoException + */ + NotifoResponse subscribeUser(String userName) throws NotifoException; + +} diff --git a/src/main/java/com/notifo/client/NotifoException.java b/src/main/java/com/notifo/client/NotifoException.java new file mode 100644 index 0000000..05d1489 --- /dev/null +++ b/src/main/java/com/notifo/client/NotifoException.java @@ -0,0 +1,18 @@ +/** + * Copyright (C) 2010 Sean P. Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +/** + * @author sscanlon + * + */ +public class NotifoException extends Exception { + + private static final long serialVersionUID = 1L; + + public NotifoException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/notifo/client/NotifoHttpClient.java b/src/main/java/com/notifo/client/NotifoHttpClient.java new file mode 100644 index 0000000..fbaf956 --- /dev/null +++ b/src/main/java/com/notifo/client/NotifoHttpClient.java @@ -0,0 +1,140 @@ +/** + * Copyright (C) 2010 Sean P. Scanlon + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HTTP; +import org.apache.http.protocol.HttpContext; + +/** + * @author sscanlon + * + */ +public class NotifoHttpClient implements NotifoClient { + + private static final String SEND_NOTIFICATION_URL = "https://api.notifo.com/v1/send_notification"; + + private static final String SUBSCRIBE_USER_URL = "https://api.notifo.com/v1/subscribe_user"; + + private static final AuthScheme AUTH_SCHEME = new BasicScheme(); + + private HttpClient httpClient = new DefaultHttpClient(); + + private final UsernamePasswordCredentials credentials; + + private final String userName; + + /** + * Construct a client with a username and apikey. "username" will be the default recipient of + * messages + * + * @param userName + * @param apiKey + */ + public NotifoHttpClient(String userName, String apiKey) { + this.userName = userName; + this.credentials = new UsernamePasswordCredentials(userName, apiKey); + ((DefaultHttpClient) httpClient).addRequestInterceptor(new HttpRequestInterceptor() { + public void process(HttpRequest request, HttpContext context) throws HttpException, + IOException { + request.addHeader(AUTH_SCHEME.authenticate(credentials, request)); + } + }); + } + + @Override + public NotifoResponse sendMessage(String messageBody) throws NotifoException { + return sendMessage(new NotifoMessage(this.userName, messageBody)); + } + + @Override + public NotifoResponse sendMessage(String to, String messageBody) throws NotifoException { + return sendMessage(new NotifoMessage(to, messageBody)); + } + + /** + * Send a message with the parameters in {@link NotifoMessage} + */ + @Override + public NotifoResponse sendMessage(NotifoMessage message) throws NotifoException { + + HttpPost post = new HttpPost(SEND_NOTIFICATION_URL); + + List nvps = new ArrayList(); + + nvps.add(new BasicNameValuePair("to", message.getTo())); + nvps.add(new BasicNameValuePair("msg", message.getMessage())); + + addPairIfNotNull(nvps, "title", message.getSubject()); + addPairIfNotNull(nvps, "uri", message.getUrl()); + addPairIfNotNull(nvps, "label", message.getLabel()); + + try { + post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); + } catch (UnsupportedEncodingException e) { + throw new NotifoException(e.getMessage(), e.getCause()); + } + + try { + HttpResponse response = httpClient.execute(post); + + return NotifoResponseFactory.parseResponse(response); + + } catch (Exception e) { + throw new NotifoException(e.getMessage(), e.getCause()); + } + } + + @Override + public NotifoResponse subscribeUser(String userName) throws NotifoException { + HttpPost post = new HttpPost(SUBSCRIBE_USER_URL); + try { + post.setEntity( + new UrlEncodedFormEntity( + Arrays.asList( + new BasicNameValuePair("username", userName)), HTTP.UTF_8) + ); + return NotifoResponseFactory.parseResponse(httpClient.execute(post)); + } catch (Exception e) { + throw new NotifoException(e.getMessage(), e.getCause()); + } + + } + + private void addPairIfNotNull(List nvps, String key, Object value) { + if (value != null) { + nvps.add(new BasicNameValuePair(key, value.toString())); + } + + } + + /** + * Override the default http client + * + * @param httpClient + */ + public void setHttpClient(HttpClient httpClient) { + this.httpClient = httpClient; + } + +} diff --git a/src/main/java/com/notifo/client/NotifoMessage.java b/src/main/java/com/notifo/client/NotifoMessage.java new file mode 100644 index 0000000..3a30565 --- /dev/null +++ b/src/main/java/com/notifo/client/NotifoMessage.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2010 Sean P. Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +/** + * @author sscanlon + * + */ +public class NotifoMessage { + + private final String to; + private final String message; + private String subject; + private String label; + private String url; + + public NotifoMessage(String to, String message) { + this.to = to; + this.message = message; + } + + /** + * @return the subject + */ + public String getSubject() { + return subject; + } + + /** + * @param subject + * the subject to set + */ + public void setSubject(String subject) { + this.subject = subject; + } + + /** + * @return the label + */ + public String getLabel() { + return label; + } + + /** + * @param label + * the label to set + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * @return the to + */ + public String getTo() { + return to; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @return the url + */ + public String getUrl() { + return url; + } + + /** + * @param url + * the url to set + */ + public void setUrl(String url) { + this.url = url; + } + +} diff --git a/src/main/java/com/notifo/client/NotifoResponse.java b/src/main/java/com/notifo/client/NotifoResponse.java new file mode 100644 index 0000000..1950f52 --- /dev/null +++ b/src/main/java/com/notifo/client/NotifoResponse.java @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2010 Sean P Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +import com.google.gson.annotations.SerializedName; + +/** + * @author sscanlon + * + */ +public class NotifoResponse { + + private static final String SUCCESS = "success"; + + private String status; + + @SerializedName("response_code") + private int responseCode; + + @SerializedName("response_message") + private String responseMessage; + + public boolean isOk() { + return SUCCESS.equalsIgnoreCase(status); + } + + @Override + public String toString() { + return new StringBuilder() + .append("status: ").append(status) + .append(", responseCode: ").append(responseCode) + .append(", responseMessage: ").append(responseMessage) + .toString(); + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public int getResponseCode() { + return responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public String getResponseMessage() { + return responseMessage; + } + + public void setResponseMessage(String responseMessage) { + this.responseMessage = responseMessage; + } + +} diff --git a/src/main/java/com/notifo/client/NotifoResponseFactory.java b/src/main/java/com/notifo/client/NotifoResponseFactory.java new file mode 100644 index 0000000..6371915 --- /dev/null +++ b/src/main/java/com/notifo/client/NotifoResponseFactory.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2010 Sean P Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; + +import com.google.gson.Gson; + +/** + * @author sscanlon + * + */ +public class NotifoResponseFactory { + + public static final Gson GSON = new Gson(); + + public static NotifoResponse parseResponse(HttpResponse response) { + NotifoResponse toReturn = null; + try { + String body = EntityUtils.toString(response.getEntity()); + toReturn = GSON.fromJson(body, NotifoResponse.class); + } catch (Exception e) { + e.printStackTrace(); + } + return toReturn; + } + +} diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties new file mode 100644 index 0000000..5fe42d8 --- /dev/null +++ b/src/main/resources/log4j.properties @@ -0,0 +1,9 @@ +# Set root logger level to DEBUG and its only appender to A1. +log4j.rootLogger=DEBUG, A1 + +# A1 is set to be a ConsoleAppender. +log4j.appender.A1=org.apache.log4j.ConsoleAppender + +# A1 uses PatternLayout. +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n diff --git a/src/test/java/com/notifo/client/NotifoHttpClientTest.java b/src/test/java/com/notifo/client/NotifoHttpClientTest.java new file mode 100644 index 0000000..a9fc231 --- /dev/null +++ b/src/test/java/com/notifo/client/NotifoHttpClientTest.java @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2010 Sean P. Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Before; +import org.junit.Test; + +/** + * @author sscanlon + * + */ +public class NotifoHttpClientTest { + + private Mockery mockery; + + private NotifoHttpClient client; + private HttpClient httpClient; + private HttpResponse mockResponse; + + @Before + public void setUp() throws Exception { + mockery = new Mockery(); + httpClient = mockery.mock(HttpClient.class); + mockResponse = mockery.mock(HttpResponse.class); + client = new NotifoHttpClient("username", "apikey"); + client.setHttpClient(httpClient); + } + + @Test(expected = NotifoException.class) + public void testSendMessageWithException() throws Exception { + + mockery.checking(new Expectations() { + { + one(httpClient).execute(with(any(HttpPost.class))); + will(throwException(new IOException("oops"))); + } + }); + + client.sendMessage("test"); + + fail("shouldn't get here"); + } + + @Test + public void testSendMessage() throws Exception { + + mockery.checking(new Expectations() { + { + exactly(3).of(httpClient).execute(with(any(HttpPost.class))); + will(returnValue(mockResponse)); + exactly(3).of(mockResponse).getEntity(); + will(returnValue( + new StringEntity("{}", "UTF-8"))); + } + }); + + client.sendMessage("test"); + client.sendMessage("user", "test"); + + NotifoMessage m = new NotifoMessage("", ""); + m.setLabel("test label"); + m.setSubject(null); + m.setUrl(null); + client.sendMessage(m); + + mockery.assertIsSatisfied(); + } + + @Test + public void testSubscribeUser() throws Exception { + mockery.checking(new Expectations() { + { + one(httpClient).execute(with(any(HttpPost.class))); + will(returnValue(mockResponse)); + one(mockResponse).getEntity(); + will(returnValue(new StringEntity("{}", "UTF-8"))); + } + }); + assertNotNull(client.subscribeUser("sps")); + mockery.assertIsSatisfied(); + } +} diff --git a/src/test/java/com/notifo/client/NotifoResponseFactoryTest.java b/src/test/java/com/notifo/client/NotifoResponseFactoryTest.java new file mode 100644 index 0000000..e519fbd --- /dev/null +++ b/src/test/java/com/notifo/client/NotifoResponseFactoryTest.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2010 Sean P Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.apache.http.HttpResponse; +import org.apache.http.entity.StringEntity; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Before; +import org.junit.Test; + +/** + * @author sscanlon + * + */ +public class NotifoResponseFactoryTest { + + private HttpResponse mockResponse; + private Mockery mockery; + + @Before + public void setUp() throws Exception { + mockery = new Mockery(); + mockResponse = mockery.mock(HttpResponse.class); + new NotifoResponseFactory(); // for code coverage :-) + } + + @Test + public void testParseResponse() throws Exception { + mockery.checking(new Expectations() { + { + one(mockResponse).getEntity(); + will(returnValue(new StringEntity("{"))); + } + }); + assertNull(NotifoResponseFactory.parseResponse(mockResponse)); + mockery.assertIsSatisfied(); + + mockery.checking(new Expectations() { + { + one(mockResponse).getEntity(); + will(returnValue(new StringEntity("{}"))); + } + }); + assertNotNull(NotifoResponseFactory.parseResponse(mockResponse)); + mockery.assertIsSatisfied(); + } +} diff --git a/src/test/java/com/notifo/client/NotifoResponseTest.java b/src/test/java/com/notifo/client/NotifoResponseTest.java new file mode 100644 index 0000000..d92930c --- /dev/null +++ b/src/test/java/com/notifo/client/NotifoResponseTest.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2010 Sean P Scanlon. + * All rights reserved. Unauthorized disclosure or distribution is prohibited. + */ +package com.notifo.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * @author sscanlon + * + */ +public class NotifoResponseTest { + + @Test + public void testBasics() throws Exception { + NotifoResponse response = new NotifoResponse(); + response.setResponseCode(200); + response.setResponseMessage("foo"); + response.setStatus("success"); + assertTrue(response.isOk()); + assertEquals(response.getResponseCode(), 200); + assertEquals(response.getStatus(), "success"); + assertEquals(response.getResponseMessage(), "foo"); + assertNotNull(response.toString()); + } + +}