├── .gitignore ├── LICENSE ├── README.md ├── RELEASE_NOTES.md ├── pom.xml └── src ├── main └── java │ └── com │ └── washingtonpost │ └── wordpress │ └── rest │ └── api │ ├── JAXRSWordPressClient.java │ ├── MockWordPressClient.java │ ├── StringUtils.java │ ├── WordPressClient.java │ ├── WordPressClientFactory.java │ ├── model │ ├── Post.java │ └── WordPressPost.java │ └── transformers │ ├── AbstractTransformer.java │ ├── Transformer.java │ └── WordPressTransformer.java └── test ├── java └── com │ └── washingtonpost │ └── wordpress │ └── rest │ └── api │ ├── IntTestJAXRSWordPressClient.java │ ├── TestJAXRSWordPressClient.java │ └── TestWordPressClientFactory.java └── resources ├── off-the-shelf-response.json └── simplelogger.properties /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2015 Google, Inc. http://angularjs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wordpress-rest-api-java-client 2 | Java Client for connecting to and parsing JSON coming our of a WordPress REST API plugin 3 | 4 | This Client uses JAX-RS to connect to a remote WordPress REST API and retrieve the lists of Posts from the /posts endpoint. The client passes through whatever query parameters sent to the `getPosts( )` method directly to the WordPress REST API. This client comes with a default implementation of a Transformer class that knows how to transform WordPress JSON into a "WordPressPost.java" POJO. 5 | 6 | If your WordPress JSON feed is customized to include fields not contained in the off-the-shelf WordPress JSON API installation, you can construct an instance of a WordPressClient with your own Transformer/POJO pair that's suitable for your organization. 7 | 8 | ## Including this JAR 9 | 10 | Add this dependency to your Maven POM: 11 | ``` 12 | 13 | com.washingtonpost.wordpress 14 | wordpress-rest-api-client 15 | x.y.z // Check RELEASE_NOTES.md for the best version for your needs 16 | 17 | ``` 18 | 19 | ## Constructing a default client with BasicAuth autentication: 20 | ``` 21 | import com.washingtonpost.wordpress.rest.api.JAXRSWordPressClient; 22 | import com.washingtonpost.wordpress.rest.api.JAXRSWordPressFactory; 23 | 24 | public class FooClazz { 25 | 26 | public void myMethod() { 27 | JAXRSWordPressClient myClient = (new JAXRSWordPressFactory()) 28 | .withBaseUrl("http://wordpress.foo.com/wp-json") 29 | .withCredentials("joe", "secret") 30 | .build(); 31 | ... 32 | } 33 | ``` 34 | 35 | ## Testing with this client 36 | This client ships with a MockWordPressClient that loads a classpath resource and returns the JSON contents of that resource as fixture data, like so: 37 | ``` 38 | import com.washingtonpost.wordpress.rest.api.model.WordPressPost; 39 | 40 | public class TestJAXRSWordPressClient { 41 | 42 | @Test 43 | public void testHappyPath() throws IOException { 44 | WordPressClient client = new WordPressClientFactory() 45 | .withMockResource("off-the-shelf-response.json") 46 | .build(); 47 | ... 48 | } 49 | ``` 50 | 51 | ## Extending Functionality 52 | To use this client to parse a different batch of JSON than comes with the standard WordPress REST API, implement 2 classes: 53 | 54 | * implement com.washingtonpost.wordpress.rest.api.model.Post // just a marker interface 55 | * extend the com.washingtonpost.wordpress.rest.api.transformers.AbstractTransformer (or just implement your own Transformer) 56 | 57 | Register your transformer with the Client when you use the ClientFactory to create your Client instance, like: 58 | ``` 59 | JAXRSWordPressClient myClient = (new JAXRSWordPressFactory()) 60 | .withTransformer(new MySpecialTransformer()) 61 | .withBaseUrl("http://wordpress.foo.com/wp-json") 62 | .build(); 63 | ``` 64 | 65 | # What this Client doesn't (yet) support 66 | 67 | * OAuth authentication 68 | * Any REST method other than a GET to the WordPress API's /posts endpoint 69 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | # wordpress-rest-api-client RELEASE NOTES 2 | 3 | ## 1.0.3 Release Date 2015/08/26 4 | 5 | * Switched to MIT License 6 | * Improving logging and test coverage 7 | 8 | ## 1.0.2 Release Date 2015/08/20 9 | 10 | * Third time's a charm on the javadoc attachment (descending now from wp-oss-parent-pom 0.0.6) 11 | 12 | ## 1.0.1 Release Date 2015/08/20 13 | 14 | * Descending from wp-oss-parent-pom 0.0.5 w/ maven-javadoc-plugin support (should fix the release problem) 15 | * This too failed... don't use this version 16 | 17 | ## 1.0.0 Release Date 2015/08/20 18 | 19 | * First implementation of a REST API Client for WordPress with "out-of-the-box" JSON-to-POJO transformation support 20 | * Failed in the final nexus 'release' phase... don't use this version 21 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.washingtonpost 7 | wp-oss-parent-pom 8 | 0.0.6 9 | 10 | 11 | com.washingtonpost.wordpress 12 | wordpress-rest-api-client 13 | 1.0.4-SNAPSHOT 14 | jar 15 | 16 | ${project.build.directory}/jacoco/coverage.exec 17 | 2.0.0 18 | 2.4 19 | 2.5.1 20 | 2.0.1 21 | 2.17 22 | 4.12 23 | 1.7.10 24 | 1.57 25 | 1.1 26 | 27 | 28 | 29 | scm:git:git@github.com:washingtonpost/wordpress-rest-api-java-client.git 30 | scm:git:git@github.com:washingtonpost/wordpress-rest-api-java-client.git 31 | HEAD 32 | https://github.com/washingtonpost/wordpress-rest-api-client.git 33 | 34 | 35 | 36 | 37 | org.slf4j 38 | slf4j-api 39 | ${version.slf4j} 40 | 41 | 42 | javax.ws.rs 43 | javax.ws.rs-api 44 | ${version.jaxrs.api} 45 | 46 | 47 | com.fasterxml.jackson.core 48 | jackson-annotations 49 | ${version.jackson} 50 | 51 | 52 | com.fasterxml.jackson.core 53 | jackson-databind 54 | ${version.jackson} 55 | 56 | 57 | commons-io 58 | commons-io 59 | ${version.commons-io} 60 | 61 | 62 | 63 | 64 | org.glassfish.jersey.core 65 | jersey-client 66 | ${version.jersey} 67 | provided 68 | 69 | 70 | 73 | 74 | junit 75 | junit 76 | ${version.junit} 77 | test 78 | 79 | 80 | org.assertj 81 | assertj-core 82 | ${version.assertj} 83 | test 84 | 85 | 86 | com.github.tomakehurst 87 | wiremock 88 | ${version.wiremock} 89 | test 90 | 91 | 92 | 93 | org.slf4j 94 | slf4j-simple 95 | ${version.slf4j} 96 | test 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-checkstyle-plugin 106 | ${version.maven.checkstyle.plugin} 107 | 108 | 109 | validate 110 | validate 111 | 112 | checkstyle.xml 113 | ${project.build.sourceEncoding} 114 | true 115 | true 116 | false 117 | 118 | 119 | check 120 | 121 | 122 | 123 | 124 | 125 | com.washingtonpost 126 | wp-java-checkstyle 127 | ${version.wp.checkstyle} 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-checkstyle-plugin 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/JAXRSWordPressClient.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import com.washingtonpost.wordpress.rest.api.model.Post; 4 | import com.washingtonpost.wordpress.rest.api.transformers.Transformer; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.StringWriter; 8 | import java.net.URI; 9 | import java.nio.charset.Charset; 10 | import java.util.List; 11 | import javax.ws.rs.client.Client; 12 | import javax.ws.rs.client.ClientBuilder; 13 | import javax.ws.rs.client.ClientRequestFilter; 14 | import javax.ws.rs.core.MediaType; 15 | import javax.ws.rs.core.Response; 16 | import javax.ws.rs.core.Response.Status; 17 | import org.apache.commons.io.IOUtils; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | /** 22 | *

Blah

23 | */ 24 | public class JAXRSWordPressClient implements WordPressClient { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(JAXRSWordPressClient.class); 27 | 28 | private final URI targetURI; 29 | private final Transformer transformer; 30 | private Client jaxrsClient; 31 | 32 | /** 33 | *

Package private constructor to encourage the caller to use this Client's associated Factory

34 | * @param targetURI The base URI of the Wordpress REST API. The {@code targetUri} should include the FQDN up to, but 35 | * not including the REST endpoints (e.g. "https://wp.foo.com/wp-json/" not 36 | * "https://wp.foo.com/wp-json/posts") 37 | * @param requestFilter A possibly null filter to pass every request this CLient makes to WordPress through. Useful for 38 | * adding BasicAuth header information to every request, if appropriate. 39 | * @param transformer A concrete class to provide transformation services from the JSON returned by the WP-API and a POJO 40 | * that implements the Post marker interface 41 | */ 42 | JAXRSWordPressClient(URI targetURI, 43 | ClientRequestFilter requestFilter, 44 | Transformer transformer) { 45 | this.targetURI = targetURI; 46 | this.transformer = transformer; 47 | this.jaxrsClient = ClientBuilder.newClient(); 48 | 49 | // If our authenticator is provided, register it as a default ClientRequestFilter so that every request this Client 50 | // makes has BasicAuth information added to it 51 | if (requestFilter != null) { 52 | jaxrsClient = jaxrsClient.register(requestFilter); 53 | } 54 | logger.debug("Constructing new JAXRSWordPressClient with targetURI '{}' and transformer '{}' " 55 | + "and requestFilter '{}'", targetURI, transformer, requestFilter); 56 | } 57 | 58 | @Override 59 | public List getPosts(String queryParams) { 60 | StringBuilder endpoint = new StringBuilder(this.targetURI.toString()).append("/posts"); 61 | if (queryParams != null && !queryParams.isEmpty()) { 62 | endpoint.append("?").append(queryParams); 63 | } 64 | 65 | logger.debug("JAXRSWordPressClient making request to '{}'", endpoint); 66 | 67 | Response response = jaxrsClient 68 | .target(endpoint.toString()) 69 | .request(MediaType.APPLICATION_JSON) 70 | .get(); 71 | 72 | if (Status.OK.getStatusCode() == response.getStatusInfo().getStatusCode()) { 73 | String json = readInputStreamToString((InputStream)response.getEntity()); 74 | try { 75 | return this.transformer.transform(json); 76 | } 77 | catch (IOException ex) { 78 | logger.error("Unable to transform JSON '{}' into a Post", json); 79 | throw new RuntimeException(ex); 80 | } 81 | } 82 | else { 83 | String errMsg = String.format("Did not get an OK status code from WordPress API at %s, rather: %s", 84 | endpoint, response.getStatusInfo()); 85 | logger.error(errMsg); 86 | throw new RuntimeException(errMsg); 87 | } 88 | } 89 | 90 | /** 91 | * Visible for testing. 92 | * @param inputStream An input stream 93 | * @return The String content of that inputStream 94 | */ 95 | String readInputStreamToString(InputStream inputStream) { 96 | StringWriter writer = new StringWriter(); 97 | try { 98 | IOUtils.copy(inputStream, writer, Charset.forName("UTF-8")); 99 | } 100 | catch (IOException ex) { 101 | throw new RuntimeException(ex); 102 | } 103 | return writer.toString(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/MockWordPressClient.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import com.washingtonpost.wordpress.rest.api.model.Post; 4 | import com.washingtonpost.wordpress.rest.api.transformers.Transformer; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.List; 8 | 9 | /** 10 | *

A Mock Client, helping for testing and localhost development

11 | */ 12 | public class MockWordPressClient implements WordPressClient { 13 | private List posts; 14 | 15 | public MockWordPressClient(String classpathFixture, Transformer transformer) { 16 | InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(classpathFixture); 17 | String jsonFixture = StringUtils.inputStreamToString(inputStream); 18 | try { 19 | this.posts = transformer.transform(jsonFixture); 20 | } 21 | catch (IOException ex) { 22 | throw new RuntimeException(ex); 23 | } 24 | } 25 | 26 | @Override 27 | public List getPosts(String queryParams) { 28 | return posts; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | 8 | /** 9 | *

10 | * Hand-jammed utility to avoid bringing in unnecessary dependencies to our wrapping classes

11 | */ 12 | final class StringUtils { 13 | private static final int BUFFER_SIZE = 8192; 14 | 15 | private StringUtils() { 16 | // shutup, checkstyle 17 | } 18 | 19 | static String inputStreamToString(InputStream inputStream) { 20 | StringBuilder sb = new StringBuilder(BUFFER_SIZE); 21 | try { 22 | BufferedReader r = new BufferedReader(new InputStreamReader(inputStream)); 23 | String str = null; 24 | while ((str = r.readLine()) != null) { 25 | sb.append(str); 26 | } 27 | } 28 | catch (IOException ioe) { 29 | throw new RuntimeException(ioe); 30 | } 31 | return sb.toString(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/WordPressClient.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import com.washingtonpost.wordpress.rest.api.model.Post; 4 | import java.util.List; 5 | 6 | /** 7 | *

The interface describing how to connect to a WordPress REST API and get a List of Posts out of it

8 | * @param

The type of the Post 9 | */ 10 | public interface WordPressClient

{ 11 | 12 | /** 13 | * @param queryParams How to query the WordPress API 14 | * @return A list of {@code P} object matching the queryParams. 15 | */ 16 | List

getPosts(String queryParams); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/WordPressClientFactory.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import com.washingtonpost.wordpress.rest.api.model.Post; 4 | import com.washingtonpost.wordpress.rest.api.transformers.Transformer; 5 | import com.washingtonpost.wordpress.rest.api.transformers.WordPressTransformer; 6 | import java.io.IOException; 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.URI; 9 | import java.net.URISyntaxException; 10 | import java.nio.charset.StandardCharsets; 11 | import javax.ws.rs.client.ClientRequestContext; 12 | import javax.ws.rs.client.ClientRequestFilter; 13 | import javax.ws.rs.core.MultivaluedMap; 14 | import javax.xml.bind.DatatypeConverter; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | *

A Factory/Builder for creating a concrete JAXRSWordPressClient

20 | *

Usage is fluent, like:

21 | *
 22 |  *     JAXRSWordPressClient myClient = (new JAXRSWordPressFactory())
 23 |  *                                          .withBaseUrl("http://wordpress.foo.com/wp-json")
 24 |  *                                          .withCredentials("joe", "secret")
 25 |  *                                          .withTransformer(new MySpecialTransformer())
 26 |  *                                          .build();
 27 |  * 
28 | *

You can also create a Mock Client by specifying a classpath resource containing an array of Post JSON, like:

29 | *
 30 |  *     MockWordPressClient myClient = (new JAXRSWordPressFactory())
 31 |  *                                          .withMockResource("/some/fixture/on/my/classpath.blah")
 32 |  *                                          .build();
 33 |  * 
34 | */ 35 | public class WordPressClientFactory { 36 | private static final Logger logger = LoggerFactory.getLogger(WordPressClientFactory.class); 37 | private ClientRequestFilter clientRequestFilter; 38 | private Transformer transformer = new WordPressTransformer(); 39 | private URI uri; 40 | private String mockResource = null; 41 | 42 | public WordPressClientFactory() { 43 | } 44 | 45 | 46 | public WordPressClientFactory withCredentials(String username, String password) { 47 | this.clientRequestFilter = new ClientRequestFilter() { 48 | private final String charset = StandardCharsets.UTF_8.name(); 49 | 50 | @Override 51 | public void filter(ClientRequestContext requestContext) throws IOException { 52 | 53 | String token = String.format("%s:%s", username, password); 54 | 55 | String basicAuthHeader = null; 56 | 57 | try { 58 | basicAuthHeader = "BASIC " + DatatypeConverter.printBase64Binary(token.getBytes(charset)); 59 | } 60 | catch (UnsupportedEncodingException ex) { 61 | throw new IllegalStateException("Cannot encode with " + charset, ex); 62 | } 63 | 64 | MultivaluedMap headers = requestContext.getHeaders(); 65 | headers.add("Authorization", basicAuthHeader); 66 | 67 | logger.trace("Added BasicAuth filter with username {}", username); 68 | } 69 | }; 70 | return this; 71 | } 72 | 73 | public WordPressClientFactory withBaseUrl(String baseUrl) { 74 | try { 75 | this.uri = new URI(baseUrl); 76 | } 77 | catch (URISyntaxException e) { 78 | logger.error("Unable to turn {} into a URI", baseUrl, e); 79 | throw new IllegalArgumentException(e); 80 | } 81 | return this; 82 | } 83 | 84 | public WordPressClientFactory withMockResource(String mockResource) { 85 | this.mockResource = mockResource; 86 | return this; 87 | } 88 | 89 | public WordPressClientFactory withTransformer(Transformer transformer) { 90 | this.transformer = transformer; 91 | return this; 92 | } 93 | 94 | /** 95 | * @return Returns an instance of a WordPressClient (by default, a JAXRSWordPressClient) 96 | */ 97 | public WordPressClient build() { 98 | if (this.mockResource != null) { 99 | return new MockWordPressClient(this.mockResource, transformer); 100 | } 101 | if (this.uri == null) { 102 | throw new IllegalStateException("A 'baseUrl' property is required when constructing a JAXRS client"); 103 | } 104 | 105 | return new JAXRSWordPressClient(this.uri, 106 | this.clientRequestFilter, 107 | this.transformer); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/model/Post.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api.model; 2 | 3 | /** 4 | *

Marker Interface for an Object that is Transformed from WordPress JSON to a Java POJO

5 | */ 6 | public interface Post { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/model/WordPressPost.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | /** 6 | *

This concrete class models the out-of-the-box fields that the WordPress REST API returns. Extend it if your organization 7 | * includes additional attributes in your plugin's response

8 | *

From the wp-api.org documentation, the following fields are the out-of-the-box values understood and modeled by this 9 | * POJO:

10 | *
 11 |  * title - Title of the post. (string) required
 12 |  * content_raw - Full text of the post. (string) required
 13 |  * excerpt_raw - Text for excerpt of the post. (string) optional
 14 |  * name - Slug of the post. (string) optional
 15 |  * status - Post status of the post: draft, publish, pending, future, private, or any custom registered status.
 16 |  *         If providing a status of future, you must specify a date in order for the post to be published as expected.
 17 |  *         Default is draft. (string) optional
 18 |  * type - Post type of the post: post, page, link, nav_menu_item, or a any custom registered type.
 19 |  *        Default is post. (string) optional
 20 |  * date - Date and time the post was, or should be, published in local time. Date should be an RFC3339 timestamp]
 21 |  *        (http://tools.ietf.org/html/rfc3339). Example: 2014-01-01T12:20:52Z. Default is the local date and time.
 22 |  *        (string) optional
 23 |  * date_gmt - Date and time the post was, or should be, published in UTC time. Date should be an RFC3339 timestamp.
 24 |  *            Example: 201401-01T12:20:52Z. Default is the current GMT date and time. (string) optional
 25 |  * author - Author of the post. Author can be provided as a string of the author’s ID or as the User object of the author.
 26 |  *          Default is current user. (object | string) optional
 27 |  * password - Password for protecting the post. Default is empty string. (string) optional
 28 |  * post_parent - Post ID of the post parent. Default is 0. (integer) optional
 29 |  * post_format - Format of the post. Default is standard. (string) optional
 30 |  * menu_order - The order in which posts specified as the page type should appear in supported menus.
 31 |  *              Default 0. (integer) optional
 32 |  * comment_status - Comment status for the post: open or closed. Indicates whether users can submit comments to the post.
 33 |  *                  Default is the option ‘default_comment_status’, or ‘closed’. (string) optional
 34 |  * ping_status - Ping status for the post: open or closed. Indicates whether users can submit pingbacks or trackbacks to
 35 |  *              the post. Default is the option ‘default_ping_status’. (string) optional
 36 |  * sticky - Sticky status for the post: true or false. Default is false. (boolean) optional
 37 |  * post_meta - Post meta entries of the post. Post meta should be an array of one or more Meta objects for each post meta
 38 |  *             entry. See the Create Meta for a Post endpoint for the key value pairs. (array) optional
 39 |  * 
40 | */ 41 | public class WordPressPost implements Post { 42 | 43 | @JsonProperty(value="title", required=true) 44 | private String title; 45 | @JsonProperty(value="ID", required=true) 46 | private long id; 47 | @JsonProperty(value="content_raw", required=true) 48 | private String contentRaw; 49 | @JsonProperty(value="excerpt_raw") 50 | private String excerptRaw; 51 | @JsonProperty(value="name") 52 | private String name; 53 | @JsonProperty("status") 54 | private String status; 55 | @JsonProperty("type") 56 | private String type; 57 | @JsonProperty("date") 58 | private String date; 59 | @JsonProperty("date_gmt") 60 | private String dateGMT; 61 | @JsonProperty("author") 62 | private String author; 63 | @JsonProperty("password") 64 | private String password; 65 | @JsonProperty("post_parent") 66 | private long postParent; 67 | @JsonProperty("post_format") 68 | private String postFormat; 69 | @JsonProperty("menu_order") 70 | private int menuOrder; 71 | @JsonProperty("comment_status") 72 | private String commentStatus; 73 | @JsonProperty("ping_status") 74 | private String pingStatus; 75 | @JsonProperty("sticky") 76 | private boolean sticky; 77 | @JsonProperty("post_meta") 78 | private String postMeta; 79 | 80 | public long getId() { 81 | return id; 82 | } 83 | 84 | public void setId(long id) { 85 | this.id = id; 86 | } 87 | 88 | public String getTitle() { 89 | return title; 90 | } 91 | 92 | public void setTitle(String title) { 93 | this.title = title; 94 | } 95 | 96 | public String getContentRaw() { 97 | return contentRaw; 98 | } 99 | 100 | public void setContentRaw(String contentRaw) { 101 | this.contentRaw = contentRaw; 102 | } 103 | 104 | public String getExcerptRaw() { 105 | return excerptRaw; 106 | } 107 | 108 | public void setExcerptRaw(String excerptRaw) { 109 | this.excerptRaw = excerptRaw; 110 | } 111 | 112 | public String getName() { 113 | return name; 114 | } 115 | 116 | public void setName(String name) { 117 | this.name = name; 118 | } 119 | 120 | public String getStatus() { 121 | return status; 122 | } 123 | 124 | public void setStatus(String status) { 125 | this.status = status; 126 | } 127 | 128 | public String getType() { 129 | return type; 130 | } 131 | 132 | public void setType(String type) { 133 | this.type = type; 134 | } 135 | 136 | public String getDate() { 137 | return date; 138 | } 139 | 140 | public void setDate(String date) { 141 | this.date = date; 142 | } 143 | 144 | public String getDateGMT() { 145 | return dateGMT; 146 | } 147 | 148 | public void setDateGMT(String dateGMT) { 149 | this.dateGMT = dateGMT; 150 | } 151 | 152 | public String getAuthor() { 153 | return author; 154 | } 155 | 156 | public void setAuthor(String author) { 157 | this.author = author; 158 | } 159 | 160 | public String getPassword() { 161 | return password; 162 | } 163 | 164 | public void setPassword(String password) { 165 | this.password = password; 166 | } 167 | 168 | public long getPostParent() { 169 | return postParent; 170 | } 171 | 172 | public void setPostParent(long postParent) { 173 | this.postParent = postParent; 174 | } 175 | 176 | public String getPostFormat() { 177 | return postFormat; 178 | } 179 | 180 | public void setPostFormat(String postFormat) { 181 | this.postFormat = postFormat; 182 | } 183 | 184 | public int getMenuOrder() { 185 | return menuOrder; 186 | } 187 | 188 | public void setMenuOrder(int menuOrder) { 189 | this.menuOrder = menuOrder; 190 | } 191 | 192 | public String getCommentStatus() { 193 | return commentStatus; 194 | } 195 | 196 | public void setCommentStatus(String commentStatus) { 197 | this.commentStatus = commentStatus; 198 | } 199 | 200 | public String getPingStatus() { 201 | return pingStatus; 202 | } 203 | 204 | public void setPingStatus(String pingStatus) { 205 | this.pingStatus = pingStatus; 206 | } 207 | 208 | public boolean isSticky() { 209 | return sticky; 210 | } 211 | 212 | public void setSticky(boolean sticky) { 213 | this.sticky = sticky; 214 | } 215 | 216 | public String getPostMeta() { 217 | return postMeta; 218 | } 219 | 220 | public void setPostMeta(String postMeta) { 221 | this.postMeta = postMeta; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/transformers/AbstractTransformer.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api.transformers; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.washingtonpost.wordpress.rest.api.model.Post; 5 | 6 | /** 7 | *

An Abstract Transformer

8 | * @param

The transformer type 9 | */ 10 | public abstract class AbstractTransformer

implements Transformer

{ 11 | 12 | protected ObjectMapper objectMapper; 13 | 14 | public AbstractTransformer() { 15 | objectMapper = new ObjectMapper(); 16 | // FIXME remove this once the Objects actually map the JSON spec 17 | // objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 18 | } 19 | 20 | public ObjectMapper getObjectMapper() { 21 | return objectMapper; 22 | } 23 | 24 | public void setObjectMapper(ObjectMapper objectMapper) { 25 | this.objectMapper = objectMapper; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/transformers/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api.transformers; 2 | 3 | import com.washingtonpost.wordpress.rest.api.model.Post; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | /** 8 | *

An interface describing how to transform JSON into a Model object

9 | * @param

The type of post this Transformer produces. 10 | */ 11 | public interface Transformer

{ 12 | 13 | List

transform(String json) throws IOException; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/washingtonpost/wordpress/rest/api/transformers/WordPressTransformer.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api.transformers; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.washingtonpost.wordpress.rest.api.model.WordPressPost; 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | /** 9 | *

Transformer for the standard WordPress response

10 | */ 11 | public class WordPressTransformer extends AbstractTransformer { 12 | 13 | @Override 14 | public List transform(String json) throws IOException { 15 | return super.objectMapper.readValue(json, new TypeReference>() {}); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/washingtonpost/wordpress/rest/api/IntTestJAXRSWordPressClient.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import static com.github.tomakehurst.wiremock.client.WireMock.*; 4 | import com.github.tomakehurst.wiremock.junit.WireMockRule; 5 | import com.washingtonpost.wordpress.rest.api.model.WordPressPost; 6 | import java.io.UnsupportedEncodingException; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.List; 9 | import javax.xml.bind.DatatypeConverter; 10 | import static org.junit.Assert.assertEquals; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | 14 | /** 15 | *

Uses WireMock to assert what the client is actually requesting on the wire of our "server"

16 | */ 17 | @SuppressWarnings("unchecked") 18 | public class IntTestJAXRSWordPressClient { 19 | 20 | private final int port = 8089; 21 | private final String url = String.format("http://localhost:%d/wp-json", port); 22 | @Rule 23 | public WireMockRule wireMockRule = new WireMockRule(port); 24 | 25 | 26 | @Test 27 | public void testUnauthenticatedGetPosts() { 28 | stubFor(get(urlMatching("/wp-json/posts\\?foo=bar")) 29 | .willReturn(aResponse() 30 | .withStatus(200) 31 | .withHeader("Content-Type", "application/json") 32 | .withBody("[{\"title\":\"Some title\"}]"))); 33 | 34 | WordPressClient client = (new WordPressClientFactory()).withBaseUrl(url).build(); 35 | 36 | List posts = client.getPosts("foo=bar"); 37 | 38 | assertEquals("Some title", posts.get(0).getTitle()); 39 | 40 | verify(getRequestedFor(urlMatching("/wp-json/posts\\?foo=bar")) 41 | .withHeader("Accept", equalTo("application/json"))); 42 | } 43 | 44 | @Test 45 | public void testAuthenticatedGetPosts() throws UnsupportedEncodingException { 46 | String token = String.format("%s:%s", "tom", "secret"); 47 | String basicAuthHeader = "BASIC " + DatatypeConverter.printBase64Binary(token.getBytes(StandardCharsets.UTF_8.name())); 48 | 49 | stubFor(get(urlMatching("/wp-json/posts\\?foo=bar")) 50 | .withHeader("Authorization", equalTo(basicAuthHeader)) 51 | .willReturn(aResponse() 52 | .withStatus(200) 53 | .withHeader("Content-Type", "application/json") 54 | .withBody("[{\"title\":\"A different title\"}]"))); 55 | 56 | WordPressClient client = (new WordPressClientFactory()) 57 | .withBaseUrl(url) 58 | .withCredentials("tom", "secret") 59 | .build(); 60 | 61 | List posts = client.getPosts("foo=bar"); 62 | 63 | assertEquals("A different title", posts.get(0).getTitle()); 64 | 65 | verify(getRequestedFor(urlMatching("/wp-json/posts\\?foo=bar")) 66 | .withHeader("Authorization", equalTo(basicAuthHeader)) 67 | .withHeader("Accept", equalTo("application/json"))); 68 | } 69 | 70 | @Test(expected=RuntimeException.class) 71 | public void testServerReturnsBadJSON() { 72 | stubFor(get(urlMatching("/wp-json/posts\\?foo=bar")) 73 | .willReturn(aResponse() 74 | .withStatus(200) 75 | .withHeader("Content-Type", "application/json") 76 | .withBody("this is not well-formed jsonb"))); 77 | 78 | WordPressClient client = (new WordPressClientFactory()).withBaseUrl(url).build(); 79 | 80 | client.getPosts("foo=bar"); 81 | } 82 | 83 | @Test(expected=RuntimeException.class) 84 | public void testBadStatusCodeThrowsRuntimeException() { 85 | stubFor(get(urlMatching("/wp-json/posts\\?foo=bar")) 86 | .willReturn(aResponse() 87 | .withStatus(500) 88 | .withBody(""))); 89 | 90 | WordPressClient client = (new WordPressClientFactory()).withBaseUrl(url).build(); 91 | 92 | client.getPosts("foo=bar"); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/com/washingtonpost/wordpress/rest/api/TestJAXRSWordPressClient.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import com.washingtonpost.wordpress.rest.api.model.WordPressPost; 4 | import java.io.ByteArrayInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.List; 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertNotNull; 11 | import org.junit.Test; 12 | 13 | /** 14 | *

Tests the JAXRSWordPressClient

15 | */ 16 | @SuppressWarnings("unchecked") 17 | public class TestJAXRSWordPressClient { 18 | 19 | @Test 20 | public void testHappyPath() throws IOException { 21 | 22 | WordPressClient client = 23 | new WordPressClientFactory().withMockResource("off-the-shelf-response.json").build(); 24 | List posts = client.getPosts("?whatever=true&foo=bar"); 25 | 26 | assertNotNull("List of posts should not be null", posts); 27 | 28 | WordPressPost post = posts.get(0); 29 | assertEquals(54, post.getId()); 30 | assertEquals("Gulp for cross-newsroom workflows", post.getTitle()); 31 | assertEquals("standard", post.getPostFormat()); 32 | assertEquals("Ernest Hemmingway", post.getAuthor()); 33 | } 34 | 35 | @Test 36 | public void testReadInputStreamToString() { 37 | String inputString = "The quick brown fox"; 38 | InputStream inputStream = new ByteArrayInputStream(inputString.getBytes(StandardCharsets.UTF_8)); 39 | 40 | JAXRSWordPressClient client = new JAXRSWordPressClient(null, null, null); 41 | String outputString = client.readInputStreamToString(inputStream); 42 | assertEquals(inputString, outputString); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/washingtonpost/wordpress/rest/api/TestWordPressClientFactory.java: -------------------------------------------------------------------------------- 1 | package com.washingtonpost.wordpress.rest.api; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.washingtonpost.wordpress.rest.api.model.Post; 5 | import com.washingtonpost.wordpress.rest.api.transformers.Transformer; 6 | import java.io.IOException; 7 | import java.util.List; 8 | import static org.junit.Assert.assertEquals; 9 | import org.junit.Test; 10 | 11 | /** 12 | *

Tests the WordPressClientFactory

13 | */ 14 | @SuppressWarnings("unchecked") 15 | public class TestWordPressClientFactory { 16 | 17 | @Test(expected=IllegalArgumentException.class) 18 | public void testFactoryChokesOnBadBaseUrl() { 19 | new WordPressClientFactory().withBaseUrl("ggg:\\bad.format").build(); 20 | } 21 | 22 | @Test 23 | public void testFactoryWithFakeBuilder() { 24 | WordPressClient client = new WordPressClientFactory() 25 | .withMockResource("off-the-shelf-response.json") 26 | .withTransformer(new FakeTransformer()) 27 | .build(); 28 | 29 | List posts = client.getPosts("?whatever=true&foo=bar"); 30 | assertEquals(1, posts.size()); 31 | assertEquals("foo", posts.get(0).value); 32 | } 33 | 34 | private class FakeTransformer implements Transformer { 35 | 36 | @Override 37 | public List transform(String json) throws IOException { 38 | return ImmutableList.of(new FakePost()); 39 | } 40 | } 41 | 42 | private class FakePost implements Post { 43 | String value = "foo"; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/resources/off-the-shelf-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 54, 4 | "title": "Gulp for cross-newsroom workflows", 5 | "content_raw": "Here's some content", 6 | "excerpt_raw": "My raw excerpt", 7 | "name": "An example post", 8 | "status": "publish", 9 | "type": "post", 10 | "date": "2015-07-27T13:13:01", 11 | "date_gmt": "2015-07-27T13:13:01", 12 | "author": "Ernest Hemmingway", 13 | "post_parent": 123, 14 | "post_format": "standard", 15 | "menu_order": 0, 16 | "comment_status": "open", 17 | "ping_status": "open", 18 | "sticky": false, 19 | "post_meta": "foo" 20 | } 21 | ] -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=debug --------------------------------------------------------------------------------