Improve the abstraction that's used for link extraction

Previously, the link extraction abstraction was inadequate as it made
assumptions about the format of the links that did not apply to all
media types. For example, it was suited to the links returned in a
application/hal+json response, but was not suited to the Atom-style
links often found in application/json responses.

This commit improves the abstraction so that the extracted links are
decoupled from the format of the response. In addition to the existing
support for extracting HAL links a new extractor for Atom-style links
has been introduced. Both extractors are now available via static
methods on the new LinkExtractors class. The sample applications have
been updated accordingly.

Closes #6
This commit is contained in:
Andy Wilkinson
2015-01-12 16:25:13 +00:00
parent bbec60bf8e
commit 78736d6c50
19 changed files with 467 additions and 29 deletions

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.core;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.util.FileCopyUtils;
/**
* Tests for {@link LinkExtractors}.
*
* @author Andy Wilkinson
*/
@RunWith(Parameterized.class)
public class LinkExtractorsTests {
private final LinkExtractor linkExtractor;
private final String linkType;
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[] { LinkExtractors.halLinks(), "hal" },
new Object[] { LinkExtractors.atomLinks(), "atom" });
}
public LinkExtractorsTests(LinkExtractor linkExtractor, String linkType) {
this.linkExtractor = linkExtractor;
this.linkType = linkType;
}
@Test
public void singleLink() throws IOException {
Map<String, List<Link>> links = this.linkExtractor
.extractLinks(createResponse("single-link"));
assertLinks(Arrays.asList(new Link("alpha", "http://alpha.example.com")), links);
}
@Test
public void multipleLinksWithDifferentRels() throws IOException {
Map<String, List<Link>> links = this.linkExtractor
.extractLinks(createResponse("multiple-links-different-rels"));
assertLinks(Arrays.asList(new Link("alpha", "http://alpha.example.com"),
new Link("bravo", "http://bravo.example.com")), links);
}
@Test
public void multipleLinksWithSameRels() throws IOException {
Map<String, List<Link>> links = this.linkExtractor
.extractLinks(createResponse("multiple-links-same-rels"));
assertLinks(Arrays.asList(new Link("alpha", "http://alpha.example.com/one"),
new Link("alpha", "http://alpha.example.com/two")), links);
}
@Test
public void noLinks() throws IOException {
Map<String, List<Link>> links = this.linkExtractor
.extractLinks(createResponse("no-links"));
assertLinks(Collections.<Link> emptyList(), links);
}
@Test
public void linksInTheWrongFormat() throws IOException {
Map<String, List<Link>> links = this.linkExtractor
.extractLinks(createResponse("wrong-format"));
assertLinks(Collections.<Link> emptyList(), links);
}
private void assertLinks(List<Link> expectedLinks, Map<String, List<Link>> actualLinks) {
Map<String, List<Link>> expectedLinksByRel = new HashMap<>();
for (Link expectedLink : expectedLinks) {
List<Link> expectedlinksWithRel = expectedLinksByRel.get(expectedLink
.getRel());
if (expectedlinksWithRel == null) {
expectedlinksWithRel = new ArrayList<>();
expectedLinksByRel.put(expectedLink.getRel(), expectedlinksWithRel);
}
expectedlinksWithRel.add(expectedLink);
}
assertEquals(expectedLinksByRel, actualLinks);
}
private MockHttpServletResponse createResponse(String contentName) throws IOException {
MockHttpServletResponse response = new MockHttpServletResponse();
FileCopyUtils.copy(new FileReader(getPayloadFile(contentName)),
response.getWriter());
return response;
}
private File getPayloadFile(String name) {
return new File("src/test/resources/link-payloads/" + linkType + "/" + name
+ ".json");
}
}

View File

@@ -0,0 +1,9 @@
{
"links": [ {
"rel": "alpha",
"href": "http://alpha.example.com"
}, {
"rel": "bravo",
"href": "http://bravo.example.com"
} ]
}

View File

@@ -0,0 +1,9 @@
{
"links": [ {
"rel": "alpha",
"href": "http://alpha.example.com/one"
}, {
"rel": "alpha",
"href": "http://alpha.example.com/two"
} ]
}

View File

@@ -0,0 +1,6 @@
{
"links": [ {
"rel": "alpha",
"href": "http://alpha.example.com"
} ]
}

View File

@@ -0,0 +1,5 @@
{
"_links": {
"alpha": ["http://alpha.example.com/one", "http://alpha.example.com/two"]
}
}

View File

@@ -0,0 +1,6 @@
{
"_links": {
"alpha": "http://alpha.example.com",
"bravo": "http://bravo.example.com"
}
}

View File

@@ -0,0 +1,5 @@
{
"_links": {
"alpha": ["http://alpha.example.com/one", "http://alpha.example.com/two"]
}
}

View File

@@ -0,0 +1,5 @@
{
"_links": {
"alpha": "http://alpha.example.com"
}
}

View File

@@ -0,0 +1,9 @@
{
"_links": [ {
"rel": "alpha",
"href": "http://alpha.example.com/one"
}, {
"rel": "alpha",
"href": "http://alpha.example.com/two"
} ]
}