Derive a link's description from its title

Previously, a LinkDescriptor had to be created with both a rel and a
description. If a description was not provided a failure would occur.

This commit relaxes the above-described restriction by allowing a
link's title to be used as its default description. If a descriptor
has a description, it will always be used irrespective of whether or
not the link has a title. If the descriptor does not have a
description and the link does have a title, the link's title will be
used. If the descriptor does not have a description and the link does
not have a title a failure will occur.

Closes gh-105
This commit is contained in:
Andy Wilkinson
2016-04-13 14:01:33 +01:00
parent 984f90fa7b
commit c4b7438708
17 changed files with 137 additions and 27 deletions

View File

@@ -51,7 +51,9 @@ class AtomLinkExtractor extends AbstractJsonLinkExtractor {
Object hrefObject = linkMap.get("href");
Object relObject = linkMap.get("rel");
if (relObject instanceof String && hrefObject instanceof String) {
return new Link((String) relObject, (String) hrefObject);
Object titleObject = linkMap.get("title");
return new Link((String) relObject, (String) hrefObject,
titleObject instanceof String ? (String) titleObject : null);
}
return null;
}

View File

@@ -67,9 +67,12 @@ class HalLinkExtractor extends AbstractJsonLinkExtractor {
private static Link maybeCreateLink(String rel, Object possibleLinkObject) {
if (possibleLinkObject instanceof Map) {
Object hrefObject = ((Map<?, ?>) possibleLinkObject).get("href");
Map<?, ?> possibleLinkMap = (Map<?, ?>) possibleLinkObject;
Object hrefObject = possibleLinkMap.get("href");
if (hrefObject instanceof String) {
return new Link(rel, (String) hrefObject);
Object titleObject = possibleLinkMap.get("title");
return new Link(rel, (String) hrefObject,
titleObject instanceof String ? (String) titleObject : null);
}
}
return null;

View File

@@ -53,6 +53,10 @@ public abstract class HypermediaDocumentation {
* If you do not want to document a link, a link descriptor can be marked as
* {@link LinkDescriptor#ignored}. This will prevent it from appearing in the
* generated snippet while avoiding the failure described above.
* <p>
* If a descriptor does not have a {@link LinkDescriptor#description(Object)
* description}, the {@link Link#getTitle() title} of the link will be used. If the
* link does not have a title a failure will occur.
*
* @param descriptors the descriptions of the response's links
* @return the snippet that will document the links

View File

@@ -29,6 +29,8 @@ public class Link {
private final String href;
private final String title;
/**
* Creates a new {@code Link} with the given {@code rel} and {@code href}.
*
@@ -36,8 +38,21 @@ public class Link {
* @param href The link's href
*/
public Link(String rel, String href) {
this(rel, href, null);
}
/**
* Creates a new {@code Link} with the given {@code rel}, {@code href}, and
* {@code title}.
*
* @param rel The link's rel
* @param href The link's href
* @param title The link's title
*/
public Link(String rel, String href, String title) {
this.rel = rel;
this.href = href;
this.title = title;
}
/**
@@ -56,12 +71,21 @@ public class Link {
return this.href;
}
/**
* Returns the link's {@code title}, or {@code null} if it does not have a title.
* @return the link's {@code title} or {@code null}
*/
public String getTitle() {
return this.title;
}
@Override
public int hashCode() {
int prime = 31;
final int prime = 31;
int result = 1;
result = prime * result + this.href.hashCode();
result = prime * result + this.rel.hashCode();
result = prime * result + ((this.title == null) ? 0 : this.title.hashCode());
return result;
}
@@ -83,13 +107,21 @@ public class Link {
if (!this.rel.equals(other.rel)) {
return false;
}
if (this.title == null) {
if (other.title != null) {
return false;
}
}
else if (!this.title.equals(other.title)) {
return false;
}
return true;
}
@Override
public String toString() {
return new ToStringCreator(this).append("rel", this.rel).append("href", this.href)
.toString();
.append("title", this.title).toString();
}
}

View File

@@ -77,12 +77,6 @@ public class LinksSnippet extends TemplatedSnippet {
this.linkExtractor = linkExtractor;
for (LinkDescriptor descriptor : descriptors) {
Assert.notNull(descriptor.getRel(), "Link descriptors must have a rel");
if (!descriptor.isIgnored()) {
Assert.notNull(descriptor.getDescription(),
"The descriptor for link '" + descriptor.getRel()
+ "' must either have a description or be" + " marked as "
+ "ignored");
}
this.descriptorsByRel.put(descriptor.getRel(), descriptor);
}
}
@@ -90,14 +84,16 @@ public class LinksSnippet extends TemplatedSnippet {
@Override
protected Map<String, Object> createModel(Operation operation) {
OperationResponse response = operation.getResponse();
Map<String, List<Link>> links;
try {
validate(this.linkExtractor.extractLinks(response));
links = this.linkExtractor.extractLinks(response);
validate(links);
}
catch (IOException ex) {
throw new ModelCreationException(ex);
}
Map<String, Object> model = new HashMap<>();
model.put("links", createLinksModel());
model.put("links", createLinksModel(links));
return model;
}
@@ -135,17 +131,48 @@ public class LinksSnippet extends TemplatedSnippet {
}
}
private List<Map<String, Object>> createLinksModel() {
private List<Map<String, Object>> createLinksModel(Map<String, List<Link>> links) {
List<Map<String, Object>> model = new ArrayList<>();
for (Entry<String, LinkDescriptor> entry : this.descriptorsByRel.entrySet()) {
LinkDescriptor descriptor = entry.getValue();
if (!descriptor.isIgnored()) {
if (descriptor.getDescription() == null) {
descriptor = createDescriptor(
getDescriptionFromLinkTitle(links, descriptor.getRel()),
descriptor);
}
model.add(createModelForDescriptor(descriptor));
}
}
return model;
}
private String getDescriptionFromLinkTitle(Map<String, List<Link>> links,
String rel) {
List<Link> linksForRel = links.get(rel);
if (linksForRel != null) {
for (Link link : linksForRel) {
if (link.getTitle() != null) {
return link.getTitle();
}
}
}
throw new SnippetException("No description was provided for the link with rel '"
+ rel + "' and no title was available from the link in the payload");
}
private LinkDescriptor createDescriptor(String description, LinkDescriptor source) {
LinkDescriptor newDescriptor = new LinkDescriptor(source.getRel())
.description(description);
if (source.isOptional()) {
newDescriptor.optional();
}
if (source.isIgnored()) {
newDescriptor.ignored();
}
return newDescriptor;
}
/**
* Returns a {@code Map} of {@link LinkDescriptor LinkDescriptors} keyed by their
* {@link LinkDescriptor#getRel() rels}.