diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml
index b681e5c5e8..de837dd322 100644
--- a/scm-webapp/pom.xml
+++ b/scm-webapp/pom.xml
@@ -154,7 +154,12 @@
resteasy-servlet-initializer
${resteasy.version}
-
+
+ de.otto.edison
+ edison-hal
+ 2.0.0-m2
+
+
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/Link.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/Link.java
deleted file mode 100644
index 7752942676..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/Link.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package sonia.scm.api.v2.resources;
-
-import lombok.Data;
-
-import javax.xml.bind.annotation.XmlRootElement;
-import java.net.URI;
-
-@Data
-@XmlRootElement
-public class Link {
-
- private URI href;
-
- public Link(URI href) {
- this.href = href;
- }
-}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java
index f37426a5e1..4071dc9342 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java
@@ -45,7 +45,7 @@ class LinkBuilder {
return new Parameters(method);
}
- public Link create() {
+ public URI create() {
if (calls.size() < classes.length) {
throw new IllegalStateException("not enough methods for all classes");
}
@@ -53,7 +53,11 @@ class LinkBuilder {
URI baseUri = uriInfo.getBaseUri();
URI relativeUri = createRelativeUri();
URI absoluteUri = baseUri.resolve(relativeUri);
- return new Link(absoluteUri);
+ return absoluteUri;
+ }
+
+ public String href() {
+ return create().toString();
}
private LinkBuilder add(String method, String[] parameters) {
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/User2UserDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/User2UserDtoMapper.java
index b7ac65b07f..72fa0db382 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/User2UserDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/User2UserDtoMapper.java
@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
+import de.otto.edison.hal.Links;
import org.apache.shiro.SecurityUtils;
import org.mapstruct.*;
import sonia.scm.api.rest.resources.UserResource;
@@ -8,10 +9,11 @@ import sonia.scm.user.User;
import javax.ws.rs.core.UriInfo;
import java.time.Instant;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Optional;
+import static de.otto.edison.hal.Link.link;
+import static de.otto.edison.hal.Links.linkingTo;
+
@Mapper
public abstract class User2UserDtoMapper {
@@ -26,14 +28,17 @@ public abstract class User2UserDtoMapper {
void appendLinks(@MappingTarget UserDto target, @Context UriInfo uriInfo) {
LinkBuilder userLinkBuilder = new LinkBuilder(uriInfo, UserV2Resource.class, UserSubResource.class);
LinkBuilder collectionLinkBuilder = new LinkBuilder(uriInfo, UserV2Resource.class, UserCollectionResource.class);
- Map links = new HashMap<>();
- links.put("self", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("get").parameters().create());
+
+ Links.Builder linksBuilder = linkingTo()
+ .self(userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("get").parameters().href());
if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) {
- links.put("delete", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("delete").parameters().create());
- links.put("update", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("update").parameters().create());
- links.put("create", collectionLinkBuilder.method("getUserCollectionResource").parameters().method("create").parameters().create());
+ linksBuilder
+ .single(link("delete", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("delete").parameters().href()))
+ .single(link("update", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("update").parameters().href()))
+ .single(link("create", collectionLinkBuilder. method("getUserCollectionResource").parameters().method("create").parameters().href()));
}
- target.setLinks(links);
+ target.add(
+ linksBuilder.build());
}
@Mapping(target = "creationDate")
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java
index 5ec6236b8e..d3dab77041 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java
@@ -76,7 +76,7 @@ public class UserCollectionResource extends AbstractManagerResource links;
+ @Override
+ protected HalRepresentation add(Links links) {
+ return super.add(links);
+ }
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java
index 31c12e8101..37c584dfdf 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkBuilderTest.java
@@ -46,8 +46,7 @@ public class LinkBuilderTest {
URI actual = builder
.method("sub")
.parameters("param_x")
- .create()
- .getHref();
+ .create();
assertEquals("http://example.com/base/main/param_x", actual.toString());
}
@@ -55,13 +54,12 @@ public class LinkBuilderTest {
public void shouldBuildPathOverSubResources() {
LinkBuilder builder = new LinkBuilder(uriInfo, Main.class, Sub.class);
- URI actual = builder
+ URI actual = builder
.method("sub")
.parameters("param_x")
.method("x")
.parameters("param_y", "param_z")
- .create()
- .getHref();
+ .create();
assertEquals("http://example.com/base/main/param_x/sub/param_y/param_z", actual.toString());
}
@@ -72,8 +70,7 @@ public class LinkBuilderTest {
URI actual = builder
.method("get")
.parameters()
- .create()
- .getHref();
+ .create();
assertEquals("http://example.com/base", actual.toString());
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/User2UserDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/User2UserDtoMapperTest.java
index b23dd7db0f..a770c4d4a8 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/User2UserDtoMapperTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/User2UserDtoMapperTest.java
@@ -15,7 +15,7 @@ import java.net.URISyntaxException;
import java.time.Instant;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -26,13 +26,12 @@ public class User2UserDtoMapperTest {
private final Subject subject = mock(Subject.class);
private ThreadState subjectThreadState = new SubjectThreadState(subject);
- private URI baseUri;
- private URI expextedBaseUri;
+ private URI expectedBaseUri;
@Before
public void init() throws URISyntaxException {
- baseUri = new URI("http://example.com/base/");
- expextedBaseUri = baseUri.resolve(UserV2Resource.USERS_PATH_V2 + "/");
+ URI baseUri = new URI("http://example.com/base/");
+ expectedBaseUri = baseUri.resolve(UserV2Resource.USERS_PATH_V2 + "/");
when(uriInfo.getBaseUri()).thenReturn(baseUri);
subjectThreadState.bind();
}
@@ -44,10 +43,10 @@ public class User2UserDtoMapperTest {
UserDto userDto = mapper.userToUserDto(user, uriInfo);
- assertEquals("expected map with self baseUri", expextedBaseUri.resolve("abc"), userDto.getLinks().get("self").getHref());
- assertEquals("expected map with delete baseUri", expextedBaseUri.resolve("abc"), userDto.getLinks().get("delete").getHref());
- assertEquals("expected map with update baseUri", expextedBaseUri.resolve("abc"), userDto.getLinks().get("update").getHref());
- assertEquals("expected map with create baseUri", expextedBaseUri, userDto.getLinks().get("create").getHref());
+ assertEquals("expected self link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("self").get().getHref());
+ assertEquals("expected delete link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("delete").get().getHref());
+ assertEquals("expected update link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("update").get().getHref());
+ assertEquals("expected create link", expectedBaseUri.toString(), userDto.getLinks().getLinkBy("create").get().getHref());
}
private User createDefaultUser() {
@@ -64,10 +63,10 @@ public class User2UserDtoMapperTest {
UserDto userDto = mapper.userToUserDto(user, uriInfo);
- assertEquals("expected map with self baseUri", expextedBaseUri.resolve("abc"), userDto.getLinks().get("self").getHref());
- assertNull("expected map without delete baseUri", userDto.getLinks().get("delete"));
- assertNull("expected map without update baseUri", userDto.getLinks().get("update"));
- assertNull("expected map without create baseUri", userDto.getLinks().get("create"));
+ assertEquals("expected self link", expectedBaseUri.resolve("abc").toString(), userDto.getLinks().getLinkBy("self").get().getHref());
+ assertFalse("expected no delete link", userDto.getLinks().getLinkBy("delete").isPresent());
+ assertFalse("expected no update link", userDto.getLinks().getLinkBy("update").isPresent());
+ assertFalse("expected no create link", userDto.getLinks().getLinkBy("create").isPresent());
}
@Test