Permalink
Browse files

Merge branch 'release/1.0.2'

Fixes #11

* release/1.0.2:
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release halbuilder-1.0.2
  Added parsing of title/name/hreflang attributes.
  Reuse link content generation
  Added travis-ci
  Added validation/contract test
  Fix link writing when theres only one link.
  Downgraded TestNG
  • Loading branch information...
talios committed Apr 12, 2012
2 parents 70a836e + f63772c commit dab54ec481084c2b96d6cea62050f47ff1339aaa
View
@@ -0,0 +1 @@
+language: java
View
@@ -3,7 +3,7 @@
<groupId>com.theoryinpractise</groupId>
<artifactId>halbuilder</artifactId>
- <version>1.0.2-SNAPSHOT</version>
+ <version>1.0.3-SNAPSHOT</version>
<description>Java based builder for the Hal specification http://stateless.co/hal_specification.html</description>
<packaging>jar</packaging>
@@ -126,7 +126,7 @@
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
- <version>6.4</version>
+ <version>6.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -83,23 +83,15 @@ public String apply(@Nullable Link link) {
for (Map.Entry<String, Collection<Link>> linkEntry : linkMap.asMap().entrySet()) {
if (linkEntry.getValue().size() == 1) {
+ Link link = linkEntry.getValue().iterator().next();
g.writeObjectFieldStart(linkEntry.getKey());
- g.writeStringField(HREF, linkEntry.getValue().iterator().next().getHref());
+ writeJsonLinkContent(g, link);
g.writeEndObject();
} else {
g.writeArrayFieldStart(linkEntry.getKey());
for (Link link : linkEntry.getValue()) {
g.writeStartObject();
- g.writeStringField(HREF, link.getHref());
- if (link.getName().isPresent()) {
- g.writeStringField(NAME, link.getName().get());
- }
- if (link.getTitle().isPresent()) {
- g.writeStringField(TITLE, link.getTitle().get());
- }
- if (link.getHreflang().isPresent()) {
- g.writeStringField(HREFLANG, link.getHreflang().get());
- }
+ writeJsonLinkContent(g, link);
g.writeEndObject();
}
g.writeEndArray();
@@ -147,4 +139,17 @@ public boolean apply(@Nullable String s) {
}
}
}
+
+ private void writeJsonLinkContent(JsonGenerator g, Link link) throws IOException {
+ g.writeStringField(HREF, link.getHref());
+ if (link.getName().isPresent()) {
+ g.writeStringField(NAME, link.getName().get());
+ }
+ if (link.getTitle().isPresent()) {
+ g.writeStringField(TITLE, link.getTitle().get());
+ }
+ if (link.getHreflang().isPresent()) {
+ g.writeStringField(HREFLANG, link.getHreflang().get());
+ }
+ }
}
@@ -1,5 +1,7 @@
package com.theoryinpractise.halbuilder.impl.json;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.theoryinpractise.halbuilder.ResourceFactory;
import com.theoryinpractise.halbuilder.impl.api.ResourceReader;
import com.theoryinpractise.halbuilder.impl.resources.MutableResource;
@@ -16,7 +18,9 @@
import static com.theoryinpractise.halbuilder.impl.api.Support.CURIE;
import static com.theoryinpractise.halbuilder.impl.api.Support.EMBEDDED;
import static com.theoryinpractise.halbuilder.impl.api.Support.HREF;
+import static com.theoryinpractise.halbuilder.impl.api.Support.HREFLANG;
import static com.theoryinpractise.halbuilder.impl.api.Support.LINKS;
+import static com.theoryinpractise.halbuilder.impl.api.Support.TITLE;
import static com.theoryinpractise.halbuilder.impl.api.Support.NAME;
@@ -81,16 +85,32 @@ private void readLinks(MutableResource resource, JsonNode rootNode) {
Iterator<JsonNode> values = keyNode.getValue().getElements();
while (values.hasNext()) {
JsonNode valueNode = values.next();
- resource.withLink(valueNode.get(HREF).asText(), keyNode.getKey());
+ withJsonLink(resource, keyNode, valueNode);
}
} else {
- resource.withLink(keyNode.getValue().get(HREF).asText(), keyNode.getKey());
+ withJsonLink(resource, keyNode, keyNode.getValue());
}
}
}
}
}
+ private void withJsonLink(MutableResource resource, Map.Entry<String, JsonNode> keyNode, JsonNode valueNode) {
+ String rel = keyNode.getKey();
+ String href = valueNode.get(HREF).asText();
+ Optional<String> name = optionalNodeValueAsText(valueNode, NAME);
+ Optional<String> title = optionalNodeValueAsText(valueNode, TITLE);
+ Optional<String> hreflang = optionalNodeValueAsText(valueNode, HREFLANG);
+ Optional<Predicate<ReadableResource>> predicate = Optional.<Predicate<ReadableResource>>absent();
+
+ resource.withLink(href, rel, predicate, name, title, hreflang );
+ }
+
+ Optional<String> optionalNodeValueAsText(JsonNode node, String key) {
+ JsonNode value = node.get(key);
+ return value != null ? Optional.of(value.asText()) : Optional.<String>absent();
+ }
+
private void readProperties(MutableResource resource, JsonNode rootNode) {
Iterator<String> fieldNames = rootNode.getFieldNames();
@@ -5,6 +5,7 @@
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -27,13 +28,16 @@
import javax.annotation.Nullable;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.regex.Pattern;
+import static com.google.common.base.Optional.fromNullable;
+import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Ordering.usingToString;
import static com.theoryinpractise.halbuilder.impl.api.Support.WHITESPACE_SPLITTER;
@@ -79,7 +83,7 @@ public Link getResourceLink() {
}
public Optional<Link> getLinkByRel(String rel) {
- return Optional.fromNullable(Iterables.getFirst(getLinksByRel(rel), null));
+ return fromNullable(Iterables.getFirst(getLinksByRel(rel), null));
}
public List<Link> getLinksByRel(final String rel) {
@@ -99,7 +103,7 @@ public Link getResourceLink() {
}
public Optional<Object> get(String name) {
- return Optional.fromNullable(properties.get(name));
+ return fromNullable(properties.get(name));
}
private List<Link> getLinksByRel(ReadableResource resource, final String curiedRel) {
@@ -122,15 +126,44 @@ public boolean apply(@Nullable Link relatable) {
for (String href : linkTable.rowKeySet()) {
Set<String> relTypes = linkTable.row(href).keySet();
- String rels = Joiner.on(" ").join(usingToString().sortedCopy(transform(relTypes, new Function<String, String>() {
+ Collection<Link> hrefLinks = linkTable.row(href).values();
+
+ // TODO I'm not sure I like this - when collating links we 'lose' the titles, names, and lang - so we
+ // combine them - it feels iki tho.
+ String rels = Joiner.on(" ").skipNulls().join(usingToString().sortedCopy(transform(relTypes, new Function<String, String>() {
public String apply(@Nullable String relType) {
return currieHref(relType);
}
})));
+ Ordering<Object> ordering = usingToString().nullsFirst();
+ Joiner joiner = Joiner.on(", ").skipNulls();
+
+ String titles = joiner.join(ordering.sortedCopy(transform(hrefLinks, new Function<Link, Object>() {
+ public Object apply(@Nullable Link link) {
+ return link.getTitle().orNull();
+ }
+ })));
+
+ String names = joiner.join(ordering.sortedCopy(transform(hrefLinks, new Function<Link, Object>() {
+ public Object apply(@Nullable Link link) {
+ return link.getName().orNull();
+ }
+ })));
+
+ String hreflangs = joiner.join(ordering.sortedCopy(transform(hrefLinks, new Function<Link, Object>() {
+ public Object apply(@Nullable Link link) {
+ return link.getHreflang().orNull();
+ }
+ })));
+
+
String curiedHref = currieHref(href);
- collatedLinks.add(new Link(resourceFactory, curiedHref, rels));
+ collatedLinks.add(new Link(resourceFactory, curiedHref, rels,
+ fromNullable(emptyToNull(names)),
+ fromNullable(emptyToNull(titles)),
+ fromNullable(emptyToNull(hreflangs))));
}
return RELATABLE_ORDERING.sortedCopy(collatedLinks);
@@ -1,12 +1,15 @@
package com.theoryinpractise.halbuilder.impl.xml;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.theoryinpractise.halbuilder.ResourceFactory;
import com.theoryinpractise.halbuilder.impl.api.ResourceReader;
import com.theoryinpractise.halbuilder.impl.resources.MutableResource;
import com.theoryinpractise.halbuilder.spi.ReadableResource;
import com.theoryinpractise.halbuilder.spi.RenderableResource;
import com.theoryinpractise.halbuilder.spi.Resource;
import com.theoryinpractise.halbuilder.spi.ResourceException;
+import org.codehaus.jackson.JsonNode;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
@@ -17,6 +20,10 @@
import java.io.Reader;
import java.util.List;
+import static com.theoryinpractise.halbuilder.impl.api.Support.HREFLANG;
+import static com.theoryinpractise.halbuilder.impl.api.Support.NAME;
+import static com.theoryinpractise.halbuilder.impl.api.Support.TITLE;
+
public class XmlResourceReader implements ResourceReader {
private ResourceFactory resourceFactory;
@@ -59,11 +66,23 @@ private void readLinks(Resource resource, Element element) {
List<Element> links = element.getChildren("link");
for (Element link : links) {
- resource.withLink(link.getAttributeValue("href"), link.getAttributeValue("rel"));
+ String rel = link.getAttributeValue("rel");
+ String href = link.getAttributeValue("href");
+ Optional<String> name = optionalElementValueAsText(link, NAME);
+ Optional<String> title = optionalElementValueAsText(link, TITLE);
+ Optional<String> hreflang = optionalElementValueAsText(link, HREFLANG);
+ Optional<Predicate<ReadableResource>> predicate = Optional.<Predicate<ReadableResource>>absent();
+
+ resource.withLink(href, rel, predicate, name, title, hreflang);
}
}
+ Optional<String> optionalElementValueAsText(Element node, String key) {
+ String value = node.getAttributeValue(key);
+ return value != null ? Optional.of(value) : Optional.<String>absent();
+ }
+
private void readProperties(Resource resource, Element element) {
List<Element> properties = element.getChildren();
for (Element property : properties) {
@@ -1,7 +1,10 @@
package com.theoryinpractise.halbuilder;
import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
import com.google.common.io.Resources;
+import com.theoryinpractise.halbuilder.spi.ReadableResource;
import com.theoryinpractise.halbuilder.spi.RenderableResource;
import com.theoryinpractise.halbuilder.spi.Resource;
import com.theoryinpractise.halbuilder.spi.ResourceException;
@@ -45,7 +48,11 @@ public void setup() throws IOException {
private Resource newBaseResource(final String href) {
return resourceFactory.newResource(href)
- .withLink("/api/customer/1234", "ns:parent");
+ .withLink("/api/customer/1234", "ns:parent",
+ Optional.<Predicate<ReadableResource>>absent(),
+ Optional.of("bob"),
+ Optional.of("The Parent"),
+ Optional.of("en") );
}
@Test
@@ -1,5 +1,6 @@
package com.theoryinpractise.halbuilder;
+import com.theoryinpractise.halbuilder.spi.Link;
import com.theoryinpractise.halbuilder.spi.ReadableResource;
import com.theoryinpractise.halbuilder.spi.ResourceException;
import org.testng.annotations.DataProvider;
@@ -40,6 +41,16 @@ public void testReader(ReadableResource resource) {
assertThat(resource.getResources()).hasSize(0);
}
+ @Test(dataProvider = "provideResources")
+ public void testLinkAttributes(ReadableResource resource) {
+ Link parent = resource.getLinkByRel("ns:parent").get();
+ assertThat(parent.getHref()).isEqualTo("https://example.com/api/customer/1234");
+ assertThat(parent.getRel()).isEqualTo("ns:parent");
+ assertThat(parent.getName().get()).isEqualTo("bob");
+ assertThat(parent.getTitle().get()).isEqualTo("The Parent");
+ assertThat(parent.getHreflang().get()).isEqualTo("en");
+ }
+
@Test(dataProvider = "provideSubResources")
public void testSubReader(ReadableResource resource) {
assertThat(resource.getResourceLink().getHref()).isEqualTo("https://example.com/api/customer/123456");
@@ -0,0 +1,59 @@
+package com.theoryinpractise.halbuilder;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.theoryinpractise.halbuilder.spi.Contract;
+import com.theoryinpractise.halbuilder.spi.ReadableResource;
+import org.testng.annotations.Test;
+
+import javax.annotation.Nullable;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ValidationTest {
+
+
+ ResourceFactory resourceFactory = new ResourceFactory();
+
+ ReadableResource resource = resourceFactory.newResource(
+ new InputStreamReader(ResourceReaderTest.class.getResourceAsStream("example.xml")));
+
+
+ public static interface Namable {
+ String getName();
+ }
+
+
+ @Test
+ public void testValidation() {
+
+ Contract noWhiteSpaceInName = new Contract() {
+ public boolean isSatisfiedBy(ReadableResource resource) {
+ return (((String) resource.get("name").or("")).matches("\\W*"));
+ }
+ };
+
+ Contract anyCharsInName = new Contract() {
+ public boolean isSatisfiedBy(ReadableResource resource) {
+ return (((String) resource.get("name").or("")).matches(".*"));
+ }
+ };
+
+ assertThat(resource.isSatisfiedBy(noWhiteSpaceInName)).isFalse();
+ assertThat(resource.isSatisfiedBy(anyCharsInName)).isTrue();
+
+ Optional<Integer> length = resource.ifSatisfiedBy(Namable.class, new Function<Namable, Integer>() {
+ public Integer apply(@Nullable Namable input) {
+ System.out.println(input.getName());
+ return input.getName().length();
+ }
+ });
+
+ assertThat(length.get()).isEqualTo(16);
+
+ }
+
+
+}
@@ -11,7 +11,10 @@
"href" : "https://example.com/api/customer/123456"
},
"ns:parent" : {
- "href" : "https://example.com/api/customer/1234"
+ "href" : "https://example.com/api/customer/1234",
+ "name" : "bob",
+ "title" : "The Parent",
+ "hreflang" : "en"
},
"ns:users" : {
"href" : "https://example.com/api/customer/123456?users"
@@ -1,5 +1,5 @@
<resource xmlns:ns="https://example.com/apidocs/accounts" xmlns:role="https://example.com/apidocs/roles" href="https://example.com/api/customer/123456">
- <link rel="ns:parent" href="https://example.com/api/customer/1234" />
+ <link rel="ns:parent" href="https://example.com/api/customer/1234" name="bob" title="The Parent" hreflang="en" />
<link rel="ns:users" href="https://example.com/api/customer/123456?users" />
<age>33</age>
<expired>false</expired>
@@ -11,7 +11,10 @@
"href" : "https://example.com/api/customer/123456"
},
"ns:parent" : {
- "href" : "https://example.com/api/customer/1234"
+ "href" : "https://example.com/api/customer/1234",
+ "name" : "bob",
+ "title" : "The Parent",
+ "hreflang" : "en"
},
"ns:users" : {
"href" : "https://example.com/api/customer/123456?users"
Oops, something went wrong.

0 comments on commit dab54ec

Please sign in to comment.