mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
Simplify link building
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
package sonia.scm.api.rest.resources;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class LinkBuilder {
|
||||||
|
private final UriInfo uriInfo;
|
||||||
|
private final Class[] classes;
|
||||||
|
private final List<Call> calls;
|
||||||
|
|
||||||
|
LinkBuilder(UriInfo uriInfo, Class... classes) {
|
||||||
|
this(uriInfo, classes, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkBuilder(UriInfo uriInfo, Class[] classes, List<Call> calls) {
|
||||||
|
this.uriInfo = uriInfo;
|
||||||
|
this.classes = classes;
|
||||||
|
this.calls = calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Parameters method(String method) {
|
||||||
|
if (calls.size() >= classes.length) {
|
||||||
|
throw new IllegalStateException("no more classes for methods");
|
||||||
|
}
|
||||||
|
return new Parameters(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parameters {
|
||||||
|
|
||||||
|
private final String method;
|
||||||
|
|
||||||
|
private Parameters(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkBuilder parameters(String... parameters) {
|
||||||
|
return LinkBuilder.this.add(method, parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkBuilder add(String method, String[] parameters) {
|
||||||
|
List<Call> newCalls = new ArrayList<>(this.calls);
|
||||||
|
newCalls.add(new Call(LinkBuilder.this.classes[calls.size()], method, parameters));
|
||||||
|
return new LinkBuilder(uriInfo, classes, newCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Link create() {
|
||||||
|
if (calls.size() < classes.length) {
|
||||||
|
throw new IllegalStateException("not enough methods for all classes");
|
||||||
|
}
|
||||||
|
|
||||||
|
URI baseUri = uriInfo.getBaseUri();
|
||||||
|
URI relativeUri = createRelativeUri();
|
||||||
|
URI absoluteUri = baseUri.resolve(relativeUri);
|
||||||
|
return new Link(absoluteUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private URI createRelativeUri() {
|
||||||
|
UriBuilder uriBuilder = userUriBuilder();
|
||||||
|
calls.forEach(call -> uriBuilder.path(call.clazz, call.method));
|
||||||
|
String[] concatenatedParameters = calls
|
||||||
|
.stream()
|
||||||
|
.map(call -> call.parameters)
|
||||||
|
.flatMap(Arrays::stream)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
return uriBuilder.build(concatenatedParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UriBuilder userUriBuilder() {
|
||||||
|
return UriBuilder.fromResource(classes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Call {
|
||||||
|
private final Class clazz;
|
||||||
|
private final String method;
|
||||||
|
private final String[] parameters;
|
||||||
|
|
||||||
|
private Call(Class clazz, String method, String[] parameters) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.method = method;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
package sonia.scm.api.rest.resources;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
class LinkMapBuilder {
|
|
||||||
private final UriInfo uriInfo;
|
|
||||||
private final Class[] classes;
|
|
||||||
private final Map<String, Link> links = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
LinkMapBuilder(UriInfo uriInfo, Class... classes) {
|
|
||||||
this.uriInfo = uriInfo;
|
|
||||||
this.classes = classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
Builder add(String linkName) {
|
|
||||||
return new ConcreteBuilder(linkName);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Builder {
|
|
||||||
Parameters method(String method);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Parameters {
|
|
||||||
Builder parameters(String... parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ConcreteBuilder implements Builder {
|
|
||||||
|
|
||||||
private final String linkName;
|
|
||||||
private final List<Call> calls = new LinkedList<>();
|
|
||||||
|
|
||||||
private int callCount = 0;
|
|
||||||
|
|
||||||
ConcreteBuilder(String linkName) {
|
|
||||||
this.linkName = linkName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Parameters method(String method) {
|
|
||||||
return new ParametersImpl(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ParametersImpl implements Parameters {
|
|
||||||
|
|
||||||
private final String method;
|
|
||||||
|
|
||||||
ParametersImpl(String method) {
|
|
||||||
this.method = method;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder parameters(String... parameters) {
|
|
||||||
return ConcreteBuilder.this.add(method, parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Builder add(String method, String[] parameters) {
|
|
||||||
this.calls.add(new Call(LinkMapBuilder.this.classes[callCount], method, parameters));
|
|
||||||
++callCount;
|
|
||||||
if (callCount >= classes.length) {
|
|
||||||
links.put(linkName, createLink());
|
|
||||||
return x -> {
|
|
||||||
throw new IllegalStateException("no more classes for methods");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Link createLink() {
|
|
||||||
URI baseUri = uriInfo.getBaseUri();
|
|
||||||
URI relativeUri = createRelativeUri();
|
|
||||||
URI absoluteUri = baseUri.resolve(relativeUri);
|
|
||||||
return new Link(absoluteUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private URI createRelativeUri() {
|
|
||||||
UriBuilder uriBuilder = userUriBuilder();
|
|
||||||
calls.forEach(call -> uriBuilder.path(call.clazz, call.method));
|
|
||||||
String[] concatenatedParameters = calls
|
|
||||||
.stream()
|
|
||||||
.map(call -> call.parameters)
|
|
||||||
.flatMap(Arrays::stream)
|
|
||||||
.toArray(String[]::new);
|
|
||||||
return uriBuilder.build(concatenatedParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private UriBuilder userUriBuilder() {
|
|
||||||
return UriBuilder.fromResource(classes[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Link> getLinkMap() {
|
|
||||||
return Collections.unmodifiableMap(links);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Call {
|
|
||||||
private final Class clazz;
|
|
||||||
private final String method;
|
|
||||||
private final String[] parameters;
|
|
||||||
|
|
||||||
private Call(Class clazz, String method, String[] parameters) {
|
|
||||||
this.clazz = clazz;
|
|
||||||
this.method = method;
|
|
||||||
this.parameters = parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,17 +24,15 @@ public abstract class User2UserDtoMapper {
|
|||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(@MappingTarget UserDto target, @Context UriInfo uriInfo) {
|
void appendLinks(@MappingTarget UserDto target, @Context UriInfo uriInfo) {
|
||||||
LinkMapBuilder userLinkBuilder = new LinkMapBuilder(uriInfo, UserNewResource.class, UserSubResource.class);
|
LinkBuilder userLinkBuilder = new LinkBuilder(uriInfo, UserNewResource.class, UserSubResource.class);
|
||||||
LinkMapBuilder collectionLinkBuilder = new LinkMapBuilder(uriInfo, UserNewResource.class, UserCollectionResource.class);
|
LinkBuilder collectionLinkBuilder = new LinkBuilder(uriInfo, UserNewResource.class, UserCollectionResource.class);
|
||||||
userLinkBuilder.add("self").method("getUserSubResource").parameters(target.getName()).method("get").parameters();
|
Map<String, Link> links = new HashMap<>();
|
||||||
|
links.put("self", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("get").parameters().create());
|
||||||
if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) {
|
if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) {
|
||||||
userLinkBuilder.add("delete").method("getUserSubResource").parameters(target.getName()).method("delete").parameters();
|
links.put("delete", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("delete").parameters().create());
|
||||||
userLinkBuilder.add("update").method("getUserSubResource").parameters(target.getName()).method("update").parameters();
|
links.put("update", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("update").parameters().create());
|
||||||
collectionLinkBuilder.add("create").method("getUserCollectionResource").parameters().method("create").parameters();
|
links.put("create", collectionLinkBuilder.method("getUserCollectionResource").parameters().method("create").parameters().create());
|
||||||
}
|
}
|
||||||
Map<String, Link> join = new HashMap<>();
|
target.setLinks(links);
|
||||||
join.putAll(userLinkBuilder.getLinkMap());
|
|
||||||
join.putAll(collectionLinkBuilder.getLinkMap());
|
|
||||||
target.setLinks(join);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,9 +73,8 @@ public class UserCollectionResource extends AbstractManagerResource<User, UserEx
|
|||||||
User user = dtoToUserMapper.userDtoToUser(userDto, "");
|
User user = dtoToUserMapper.userDtoToUser(userDto, "");
|
||||||
manager.create(user);
|
manager.create(user);
|
||||||
|
|
||||||
LinkMapBuilder builder = new LinkMapBuilder(uriInfo, UserNewResource.class, UserSubResource.class);
|
LinkBuilder builder = new LinkBuilder(uriInfo, UserNewResource.class, UserSubResource.class);
|
||||||
builder.add("self").method("getUserSubResource").parameters(user.getName()).method("get").parameters();
|
return Response.created(builder.method("getUserSubResource").parameters(user.getName()).method("get").parameters().create().getHref()).build();
|
||||||
return Response.created(builder.getLinkMap().get("self").getHref()).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class LinkMapBuilderTest {
|
public class LinkBuilderTest {
|
||||||
|
|
||||||
@Path("base")
|
@Path("base")
|
||||||
public static class Main {
|
public static class Main {
|
||||||
@@ -41,52 +41,60 @@ public class LinkMapBuilderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldBuildSimplePath() {
|
public void shouldBuildSimplePath() {
|
||||||
LinkMapBuilder builder = new LinkMapBuilder(uriInfo, Main.class);
|
LinkBuilder builder = new LinkBuilder(uriInfo, Main.class);
|
||||||
builder
|
|
||||||
.add("link")
|
|
||||||
.method("sub")
|
|
||||||
.parameters("param_x");
|
|
||||||
|
|
||||||
URI actual = builder.getLinkMap().get("link").getHref();
|
URI actual = builder
|
||||||
|
.method("sub")
|
||||||
|
.parameters("param_x")
|
||||||
|
.create()
|
||||||
|
.getHref();
|
||||||
assertEquals("http://example.com/base/main/param_x", actual.toString());
|
assertEquals("http://example.com/base/main/param_x", actual.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldBuildPathOverSubResources() {
|
public void shouldBuildPathOverSubResources() {
|
||||||
LinkMapBuilder builder = new LinkMapBuilder(uriInfo, Main.class, Sub.class);
|
LinkBuilder builder = new LinkBuilder(uriInfo, Main.class, Sub.class);
|
||||||
builder
|
|
||||||
.add("link")
|
URI actual = builder
|
||||||
.method("sub")
|
.method("sub")
|
||||||
.parameters("param_x")
|
.parameters("param_x")
|
||||||
.method("x")
|
.method("x")
|
||||||
.parameters("param_y", "param_z");
|
.parameters("param_y", "param_z")
|
||||||
|
.create()
|
||||||
URI actual = builder.getLinkMap().get("link").getHref();
|
.getHref();
|
||||||
assertEquals("http://example.com/base/main/param_x/sub/param_y/param_z", actual.toString());
|
assertEquals("http://example.com/base/main/param_x/sub/param_y/param_z", actual.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldBuildPathWithoutParameters() {
|
public void shouldBuildPathWithoutParameters() {
|
||||||
LinkMapBuilder builder = new LinkMapBuilder(uriInfo, NoParam.class);
|
LinkBuilder builder = new LinkBuilder(uriInfo, NoParam.class);
|
||||||
builder
|
|
||||||
.add("link")
|
|
||||||
.method("get")
|
|
||||||
.parameters();
|
|
||||||
|
|
||||||
URI actual = builder.getLinkMap().get("link").getHref();
|
URI actual = builder
|
||||||
|
.method("get")
|
||||||
|
.parameters()
|
||||||
|
.create()
|
||||||
|
.getHref();
|
||||||
assertEquals("http://example.com/base", actual.toString());
|
assertEquals("http://example.com/base", actual.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void shouldFailForTooManyMethods() {
|
public void shouldFailForTooManyMethods() {
|
||||||
LinkMapBuilder builder = new LinkMapBuilder(uriInfo, Main.class);
|
LinkBuilder builder = new LinkBuilder(uriInfo, Main.class);
|
||||||
builder
|
builder
|
||||||
.add("link")
|
|
||||||
.method("sub")
|
.method("sub")
|
||||||
.parameters("param_x")
|
.parameters("param_x")
|
||||||
.method("x");
|
.method("x");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void shouldFailForTooFewMethods() {
|
||||||
|
LinkBuilder builder = new LinkBuilder(uriInfo, Main.class, Sub.class);
|
||||||
|
builder
|
||||||
|
.method("sub")
|
||||||
|
.parameters("param_x")
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setBaseUri() throws URISyntaxException {
|
public void setBaseUri() throws URISyntaxException {
|
||||||
when(uriInfo.getBaseUri()).thenReturn(new URI("http://example.com/"));
|
when(uriInfo.getBaseUri()).thenReturn(new URI("http://example.com/"));
|
||||||
Reference in New Issue
Block a user