#787 - Return all Link attributes on discovery.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-2019 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -39,6 +39,7 @@ public class CollectionJsonLinkDiscoverer extends JsonPathLinkDiscoverer {
|
||||
private final CollectionJsonSelfLinkDiscoverer selfLinkDiscoverer;
|
||||
|
||||
public CollectionJsonLinkDiscoverer() {
|
||||
|
||||
super("$.collection..links..[?(@.rel == '%s')].href", MediaTypes.COLLECTION_JSON);
|
||||
this.selfLinkDiscoverer = new CollectionJsonSelfLinkDiscoverer();
|
||||
}
|
||||
@@ -117,6 +118,7 @@ public class CollectionJsonLinkDiscoverer extends JsonPathLinkDiscoverer {
|
||||
* {@link JsonPathLinkDiscoverer} that looks for the non-parameterized {@literal collection.href} link.
|
||||
*/
|
||||
private static class CollectionJsonSelfLinkDiscoverer extends JsonPathLinkDiscoverer {
|
||||
|
||||
CollectionJsonSelfLinkDiscoverer() {
|
||||
super("$.collection.href", MediaTypes.COLLECTION_JSON);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
@@ -19,16 +19,16 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minidev.json.JSONArray;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -40,6 +40,7 @@ import com.jayway.jsonpath.JsonPath;
|
||||
* {@link LinkDiscoverer} that uses {@link JsonPath} to find links inside a representation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
public class JsonPathLinkDiscoverer implements LinkDiscoverer {
|
||||
|
||||
@@ -74,24 +75,15 @@ public class JsonPathLinkDiscoverer implements LinkDiscoverer {
|
||||
* The template has to contain a single {@code %s} placeholder which will be replaced by the relation type.
|
||||
*
|
||||
* @param pathTemplate must not be {@literal null} or empty and contain a single placeholder.
|
||||
* @param mediaType the primary {@link MediaType}s to support.
|
||||
* @param others {@link MediaType}s to support.
|
||||
* @param mediaTypes the {@link MediaType}s to support.
|
||||
*/
|
||||
public JsonPathLinkDiscoverer(String pathTemplate, MediaType mediaType, MediaType... others) {
|
||||
public JsonPathLinkDiscoverer(String pathTemplate, MediaType... mediaTypes) {
|
||||
|
||||
Assert.hasText(pathTemplate, "Path template must not be null!");
|
||||
// Assert.isTrue(StringUtils.countOccurrencesOf(pathTemplate, "%s") == 1,
|
||||
// "Path template must contain a single placeholder!");
|
||||
Assert.notNull(mediaType, "Primary MediaType must not be null!");
|
||||
Assert.notNull(others, "Other MediaTypes must not be null!");
|
||||
Assert.notNull(mediaTypes, "Primary MediaType must not be null!");
|
||||
|
||||
this.pathTemplate = pathTemplate;
|
||||
|
||||
List<MediaType> mediaTypes = new ArrayList<>(others.length + 1);
|
||||
mediaTypes.add(mediaType);
|
||||
mediaTypes.addAll(Arrays.asList(others));
|
||||
|
||||
this.mediaTypes = mediaTypes;
|
||||
this.mediaTypes = Arrays.asList(mediaTypes);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -167,16 +159,33 @@ public class JsonPathLinkDiscoverer implements LinkDiscoverer {
|
||||
|
||||
if (parseResult instanceof JSONArray) {
|
||||
|
||||
JSONArray array = (JSONArray) parseResult;
|
||||
JSONArray jsonArray = (JSONArray) parseResult;
|
||||
|
||||
return array.stream() //
|
||||
.map(element -> new Link(element.toString(), rel)) //
|
||||
return jsonArray.stream() //
|
||||
.flatMap(element -> (element instanceof JSONArray)
|
||||
? ((JSONArray) element).stream()
|
||||
: Stream.of(element))
|
||||
.map(element -> extractLink(element, rel)) //
|
||||
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
|
||||
} else if (parseResult instanceof Map) {
|
||||
|
||||
return Collections.singletonList(extractLink(parseResult, rel));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(Collections.singletonList(new Link(parseResult.toString(), rel)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for each {@link LinkDiscoverer} to extract relevant attributes and generate a {@link Link}.
|
||||
*
|
||||
* @param element
|
||||
* @param rel
|
||||
* @return link
|
||||
*/
|
||||
protected Link extractLink(Object element, String rel) {
|
||||
return new Link(element.toString(), rel);
|
||||
};
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2019 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.
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.hateoas.hal;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.core.JsonPathLinkDiscoverer;
|
||||
@@ -23,10 +26,35 @@ import org.springframework.hateoas.core.JsonPathLinkDiscoverer;
|
||||
* {@link LinkDiscoverer} implementation based on HAL link structure.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
public class HalLinkDiscoverer extends JsonPathLinkDiscoverer {
|
||||
|
||||
/**
|
||||
* Constructor for {@link MediaTypes#HAL_JSON}.
|
||||
*/
|
||||
public HalLinkDiscoverer() {
|
||||
super("$._links..['%s']..href", MediaTypes.HAL_JSON);
|
||||
super("$._links..['%s']", MediaTypes.HAL_JSON, MediaTypes.HAL_JSON_UTF8);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Link extractLink(Object element, String rel) {
|
||||
|
||||
if (element instanceof Map) {
|
||||
|
||||
Map<String, String> json = (Map<String, String>) element;
|
||||
|
||||
return new Link(json.get("href"), rel)
|
||||
.withHreflang(json.get("hreflang"))
|
||||
.withMedia(json.get("media"))
|
||||
.withTitle(json.get("title"))
|
||||
.withType(json.get("type"))
|
||||
.withDeprecation(json.get("deprecation"))
|
||||
.withProfile(json.get("profile"))
|
||||
.withName(json.get("name"));
|
||||
}
|
||||
|
||||
return super.extractLink(element, rel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-2019 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.
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.hateoas.hal.forms;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.core.JsonPathLinkDiscoverer;
|
||||
|
||||
@@ -26,6 +29,27 @@ import org.springframework.hateoas.core.JsonPathLinkDiscoverer;
|
||||
public class HalFormsLinkDiscoverer extends JsonPathLinkDiscoverer {
|
||||
|
||||
public HalFormsLinkDiscoverer() {
|
||||
super("$._links..['%s']..href", MediaTypes.HAL_FORMS_JSON);
|
||||
super("$._links..['%s']", MediaTypes.HAL_FORMS_JSON);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Link extractLink(Object element, String rel) {
|
||||
|
||||
if (element instanceof Map) {
|
||||
|
||||
Map<String, String> json = (Map<String, String>) element;
|
||||
|
||||
return new Link(json.get("href"), rel)
|
||||
.withHreflang(json.get("hreflang"))
|
||||
.withMedia(json.get("media"))
|
||||
.withTitle(json.get("title"))
|
||||
.withType(json.get("type"))
|
||||
.withDeprecation(json.get("deprecation"))
|
||||
.withProfile(json.get("profile"))
|
||||
.withName(json.get("name"));
|
||||
}
|
||||
|
||||
return super.extractLink(element, rel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
* Copyright 2013-2019 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.hateoas.core;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@@ -30,16 +29,4 @@ public class JsonPathLinkDiscovererUnitTest {
|
||||
public void rejectsNullPattern() {
|
||||
new JsonPathLinkDiscoverer(null, MediaType.ALL);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsPatternWithWithoutPlaceholder() {
|
||||
new JsonPathLinkDiscoverer("$links", MediaType.ALL);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsPatternWithMultiplePlaceholders() {
|
||||
new JsonPathLinkDiscoverer("$links%s%s", MediaType.ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2019 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.
|
||||
@@ -16,24 +16,28 @@
|
||||
package org.springframework.hateoas.hal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.hateoas.support.MappingUtils.read;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.hateoas.IanaLinkRelation;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.core.AbstractLinkDiscovererUnitTest;
|
||||
import org.springframework.hateoas.support.MappingUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HalLinkDiscoverer}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
public class HalLinkDiscovererUnitTest extends AbstractLinkDiscovererUnitTest {
|
||||
|
||||
static final LinkDiscoverer discoverer = new HalLinkDiscoverer();
|
||||
static final String SAMPLE = "{ _links : { self : { href : 'selfHref' }, " + //
|
||||
"relation : [ { href : 'firstHref' }, { href : 'secondHref' }], " + //
|
||||
"'http://foo.com/bar' : { href : 'fullRelHref' }, " + "}}";
|
||||
|
||||
/**
|
||||
* @see #314
|
||||
@@ -41,12 +45,35 @@ public class HalLinkDiscovererUnitTest extends AbstractLinkDiscovererUnitTest {
|
||||
@Test
|
||||
public void discoversFullyQualifiedRel() {
|
||||
|
||||
Link link = getDiscoverer().findLinkWithRel("http://foo.com/bar", SAMPLE);
|
||||
Link link = getDiscoverer().findLinkWithRel("http://foo.com/bar", getInputString());
|
||||
|
||||
assertThat(link).isNotNull();
|
||||
assertThat(link.getHref()).isEqualTo("fullRelHref");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #787
|
||||
*/
|
||||
@Test
|
||||
public void discoversAllTheLinkAttributes() throws IOException {
|
||||
|
||||
String linkText = read(new ClassPathResource("hal-link.json", getClass()));
|
||||
|
||||
Link actual = getDiscoverer().findLinkWithRel(IanaLinkRelation.SELF.value(), linkText);
|
||||
|
||||
Link expected = Link.valueOf("</customer/1>;" //
|
||||
+ "rel=\"self\";" //
|
||||
+ "hreflang=\"en\";" //
|
||||
+ "media=\"pdf\";" //
|
||||
+ "title=\"pdf customer copy\";" //
|
||||
+ "type=\"portable document\";" //
|
||||
+ "deprecation=\"http://example.com/customers/deprecated\";" //
|
||||
+ "profile=\"my-profile\"" //
|
||||
+ "name=\"my-name\"");
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #470
|
||||
*/
|
||||
@@ -62,7 +89,12 @@ public class HalLinkDiscovererUnitTest extends AbstractLinkDiscovererUnitTest {
|
||||
|
||||
@Override
|
||||
protected String getInputString() {
|
||||
return SAMPLE;
|
||||
|
||||
try {
|
||||
return read(new ClassPathResource("hal-link-discoverer.json", getClass()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2019 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.
|
||||
@@ -15,11 +15,16 @@
|
||||
*/
|
||||
package org.springframework.hateoas.hal.forms;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.hateoas.support.MappingUtils.read;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.hateoas.IanaLinkRelation;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.core.AbstractLinkDiscovererUnitTest;
|
||||
|
||||
@@ -31,16 +36,36 @@ import org.springframework.hateoas.core.AbstractLinkDiscovererUnitTest;
|
||||
public class HalFormsLinkDiscovererUnitTest extends AbstractLinkDiscovererUnitTest {
|
||||
|
||||
static final LinkDiscoverer discoverer = new HalFormsLinkDiscoverer();
|
||||
static final String SAMPLE = "{ _links : { self : { href : 'selfHref' }, " + //
|
||||
"relation : [ { href : 'firstHref' }, { href : 'secondHref' }], " + //
|
||||
"'http://foo.com/bar' : { href : 'fullRelHref' }, " + "}}";
|
||||
|
||||
/**
|
||||
* @see #314
|
||||
*/
|
||||
@Test
|
||||
public void discoversFullyQualifiedRel() {
|
||||
assertThat(getDiscoverer().findLinkWithRel("http://foo.com/bar", SAMPLE), is(notNullValue()));
|
||||
assertThat(getDiscoverer().findLinkWithRel("http://foo.com/bar", getInputString())).isNotNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #787
|
||||
*/
|
||||
@Test
|
||||
public void discoversAllTheLinkAttributes() throws IOException {
|
||||
|
||||
String linkText = read(new ClassPathResource("hal-forms-link.json", getClass()));
|
||||
|
||||
Link actual = getDiscoverer().findLinkWithRel(IanaLinkRelation.SELF.value(), linkText);
|
||||
|
||||
Link expected = Link.valueOf("</customer/1>;" //
|
||||
+ "rel=\"self\";" //
|
||||
+ "hreflang=\"en\";" //
|
||||
+ "media=\"pdf\";" //
|
||||
+ "title=\"pdf customer copy\";" //
|
||||
+ "type=\"portable document\";" //
|
||||
+ "deprecation=\"http://example.com/customers/deprecated\";" //
|
||||
+ "profile=\"my-profile\"" //
|
||||
+ "name=\"my-name\"");
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,7 +75,12 @@ public class HalFormsLinkDiscovererUnitTest extends AbstractLinkDiscovererUnitTe
|
||||
|
||||
@Override
|
||||
protected String getInputString() {
|
||||
return SAMPLE;
|
||||
|
||||
try {
|
||||
return read(new ClassPathResource("hal-forms-link-discoverer.json", getClass()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "selfHref"
|
||||
},
|
||||
"relation": [
|
||||
{
|
||||
"href": "firstHref"
|
||||
},
|
||||
{
|
||||
"href": "secondHref"
|
||||
}
|
||||
],
|
||||
"http://foo.com/bar": {
|
||||
"href": "fullRelHref"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"_links" : {
|
||||
"self" : {
|
||||
"href" : "/customer/1",
|
||||
"hreflang" : "en",
|
||||
"media" : "pdf",
|
||||
"title" : "pdf customer copy",
|
||||
"type" : "portable document",
|
||||
"deprecation" : "http://example.com/customers/deprecated",
|
||||
"profile" : "my-profile",
|
||||
"name" : "my-name"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "selfHref"
|
||||
},
|
||||
"relation": [
|
||||
{
|
||||
"href": "firstHref"
|
||||
},
|
||||
{
|
||||
"href": "secondHref"
|
||||
}
|
||||
],
|
||||
"http://foo.com/bar": {
|
||||
"href": "fullRelHref"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"_links" : {
|
||||
"self" : {
|
||||
"href" : "/customer/1",
|
||||
"hreflang" : "en",
|
||||
"media" : "pdf",
|
||||
"title" : "pdf customer copy",
|
||||
"type" : "portable document",
|
||||
"deprecation" : "http://example.com/customers/deprecated",
|
||||
"profile" : "my-profile",
|
||||
"name" : "my-name"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user