Skip to content

#678 - Add 'profile' to Link #679

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/main/java/org/springframework/hateoas/Link.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
*
* @author Oliver Gierke
* @author Greg Turnquist
* @author Jens Schauder
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(value = "templated", ignoreUnknown = true)
Expand All @@ -53,6 +54,10 @@ public class Link implements Serializable {
private static final long serialVersionUID = -9037755944661782121L;
private static final String URI_PATTERN = "(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";

private static final Pattern URI_AND_ATTRIBUTES_PATTERN = Pattern.compile("<(.*)>;(.*)");
private static final Pattern KEY_AND_VALUE_PATTERN = Pattern
.compile("(\\w+)=\"(\\p{Lower}[\\p{Lower}\\p{Digit}\\.\\-\\s]*|" + URI_PATTERN + ")\"");

public static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";

public static final String REL_SELF = "self";
Expand All @@ -68,6 +73,7 @@ public class Link implements Serializable {
private @Wither String title;
private @Wither String type;
private @Wither String deprecation;
private @Wither String profile;
private @JsonIgnore UriTemplate template;
private @JsonIgnore List<Affordance> affordances;

Expand Down Expand Up @@ -183,7 +189,7 @@ public Link andAffordances(List<Affordance> affordances) {
public Link withAffordances(List<Affordance> affordances) {

return new Link(this.rel, this.href, this.hreflang, this.media, this.title, this.type, this.deprecation,
this.template, affordances);
this.profile, this.template, affordances);
}

/**
Expand Down Expand Up @@ -298,6 +304,10 @@ public String toString() {
linkString += ";deprecation=\"" + deprecation + "\"";
}

if (profile != null) {
linkString += ";profile=\"" + profile + "\"";
}

return linkString;
}

Expand All @@ -316,8 +326,7 @@ public static Link valueOf(String element) {
return null;
}

Pattern uriAndAttributes = Pattern.compile("<(.*)>;(.*)");
Matcher matcher = uriAndAttributes.matcher(element);
Matcher matcher = URI_AND_ATTRIBUTES_PATTERN.matcher(element);

if (matcher.find()) {

Expand Down Expand Up @@ -349,6 +358,10 @@ public static Link valueOf(String element) {
link = link.withDeprecation(attributes.get("deprecation"));
}

if (attributes.containsKey("profile")) {
link = link.withProfile(attributes.get("profile"));
}

return link;

} else {
Expand All @@ -369,9 +382,7 @@ private static Map<String, String> getAttributeMap(String source) {
}

Map<String, String> attributes = new HashMap<String, String>();
Pattern keyAndValue = Pattern
.compile("(\\w+)=\"(\\p{Lower}[\\p{Lower}\\p{Digit}\\.\\-\\s]*|" + URI_PATTERN + ")\"");
Matcher matcher = keyAndValue.matcher(source);
Matcher matcher = KEY_AND_VALUE_PATTERN.matcher(source);

while (matcher.find()) {
attributes.put(matcher.group(1), matcher.group(2));
Expand Down
100 changes: 65 additions & 35 deletions src/test/java/org/springframework/hateoas/LinkUnitTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
Expand All @@ -22,6 +22,7 @@
import java.util.List;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
Expand All @@ -32,20 +33,27 @@
*
* @author Oliver Gierke
* @author Greg Turnquist
* @author Jens Schauder
*/
public class LinkUnitTest {

@Test
public void linkWithHrefOnlyBecomesSelfLink() {

Link link = new Link("foo");
assertThat(link.getRel()).isEqualTo(Link.REL_SELF);
}

@Test
public void createsLinkFromRelAndHref() {

Link link = new Link("foo", Link.REL_SELF);
assertThat(link.getHref()).isEqualTo("foo");
assertThat(link.getRel()).isEqualTo(Link.REL_SELF);

SoftAssertions.assertSoftly(softly -> {

softly.assertThat(link.getHref()).isEqualTo("foo");
softly.assertThat(link.getRel()).isEqualTo(Link.REL_SELF);
});
}

@Test(expected = IllegalArgumentException.class)
Expand Down Expand Up @@ -103,32 +111,41 @@ public void differentTypeDoesNotEqual() {
@Test
public void returnsNullForNullOrEmptyLink() {

assertThat(Link.valueOf(null)).isNull();
assertThat(Link.valueOf("")).isNull();
SoftAssertions.assertSoftly(softly -> {

softly.assertThat(Link.valueOf(null)).isNull();
softly.assertThat(Link.valueOf("")).isNull();
});
}

/**
* @see #54
* @see #100
* @see #678
*/
@Test
public void parsesRFC5988HeaderIntoLink() {

assertThat(Link.valueOf("</something>;rel=\"foo\"")).isEqualTo(new Link("/something", "foo"));
assertThat(Link.valueOf("</something>;rel=\"foo\";title=\"Some title\"")).isEqualTo(new Link("/something", "foo"));
assertThat(Link.valueOf("</customer/1>;" //
+ "rel=\"self\";" //
+ "hreflang=\"en\";" //
+ "media=\"pdf\";" //
+ "title=\"pdf customer copy\";" //
+ "type=\"portable document\";" //
+ "deprecation=\"http://example.com/customers/deprecated\"")) //
.isEqualTo(new Link("/customer/1") //
.withHreflang("en") //
.withMedia("pdf") //
.withTitle("pdf customer copy") //
.withType("portable document") //
.withDeprecation("http://example.com/customers/deprecated"));
SoftAssertions.assertSoftly(softly -> {

softly.assertThat(Link.valueOf("</something>;rel=\"foo\"")).isEqualTo(new Link("/something", "foo"));
softly.assertThat(Link.valueOf("</something>;rel=\"foo\";title=\"Some title\""))
.isEqualTo(new Link("/something", "foo"));
softly.assertThat(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\"")) //
.isEqualTo(new Link("/customer/1") //
.withHreflang("en") //
.withMedia("pdf") //
.withTitle("pdf customer copy") //
.withType("portable document") //
.withDeprecation("http://example.com/customers/deprecated").withProfile("my-profile"));
});
}

/**
Expand All @@ -138,8 +155,11 @@ public void parsesRFC5988HeaderIntoLink() {
public void ignoresUnrecognizedAttributes() {
Link link = Link.valueOf("</something>;rel=\"foo\";unknown=\"should fail\"");

assertThat(link.getHref()).isEqualTo("/something");
assertThat(link.getRel()).isEqualTo("foo");
SoftAssertions.assertSoftly(softly -> {

softly.assertThat(link.getHref()).isEqualTo("/something");
softly.assertThat(link.getRel()).isEqualTo("foo");
});
}

@Test(expected = IllegalArgumentException.class)
Expand All @@ -165,10 +185,13 @@ public void isTemplatedIfSourceContainsTemplateVariables() {

Link link = new Link("/foo{?page}");

assertThat(link.isTemplated()).isTrue();
assertThat(link.getVariableNames()).hasSize(1);
assertThat(link.getVariableNames()).contains("page");
assertThat(link.expand("2")).isEqualTo(new Link("/foo?page=2"));
SoftAssertions.assertSoftly(softly -> {

softly.assertThat(link.isTemplated()).isTrue();
softly.assertThat(link.getVariableNames()).hasSize(1);
softly.assertThat(link.getVariableNames()).contains("page");
softly.assertThat(link.expand("2")).isEqualTo(new Link("/foo?page=2"));
});
}

/**
Expand All @@ -179,8 +202,11 @@ public void isntTemplatedIfSourceDoesNotContainTemplateVariables() {

Link link = new Link("/foo");

assertThat(link.isTemplated()).isFalse();
assertThat(link.getVariableNames()).hasSize(0);
SoftAssertions.assertSoftly(softly -> {

softly.assertThat(link.isTemplated()).isFalse();
softly.assertThat(link.getVariableNames()).hasSize(0);
});
}

/**
Expand Down Expand Up @@ -211,6 +237,7 @@ public void keepsCompleteBaseUri() {
*/
@Test
public void parsesLinkRelationWithDotAndMinus() {

assertThat(Link.valueOf("<http://localhost>; rel=\"rel-with-minus-and-.\"").getRel())
.isEqualTo("rel-with-minus-and-.");
}
Expand All @@ -235,15 +262,18 @@ public void linkWithAffordancesShouldWorkProperly() {
Link linkWithAffordance = originalLink.andAffordance(new TestAffordance());
Link linkWithTwoAffordances = linkWithAffordance.andAffordance(new TestAffordance());

assertThat(originalLink.getAffordances()).hasSize(0);
assertThat(linkWithAffordance.getAffordances()).hasSize(1);
assertThat(linkWithTwoAffordances.getAffordances()).hasSize(2);
SoftAssertions.assertSoftly(softly -> {

softly.assertThat(originalLink.getAffordances()).hasSize(0);
softly.assertThat(linkWithAffordance.getAffordances()).hasSize(1);
softly.assertThat(linkWithTwoAffordances.getAffordances()).hasSize(2);

assertThat(originalLink.hashCode()).isNotEqualTo(linkWithAffordance.hashCode());
assertThat(originalLink).isNotEqualTo(linkWithAffordance);
softly.assertThat(originalLink.hashCode()).isNotEqualTo(linkWithAffordance.hashCode());
softly.assertThat(originalLink).isNotEqualTo(linkWithAffordance);

assertThat(linkWithAffordance.hashCode()).isNotEqualTo(linkWithTwoAffordances.hashCode());
assertThat(linkWithAffordance).isNotEqualTo(linkWithTwoAffordances);
softly.assertThat(linkWithAffordance.hashCode()).isNotEqualTo(linkWithTwoAffordances.hashCode());
softly.assertThat(linkWithAffordance).isNotEqualTo(linkWithTwoAffordances);
});
}

/**
Expand Down