Generate repository links in dto

This commit is contained in:
René Pfeuffer
2018-07-03 14:29:48 +02:00
parent 1bcc35d48b
commit 150838be85
6 changed files with 137 additions and 9 deletions

View File

@@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
import lombok.Setter;
@@ -26,4 +27,10 @@ public class RepositoryDto extends HalRepresentation {
private boolean publicReadable = false;
private boolean archived = false;
private String type;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
}
}

View File

@@ -1,10 +1,17 @@
package sonia.scm.api.v2.resources;
import com.google.inject.Inject;
import de.otto.edison.hal.Links;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import sonia.scm.repository.HealthCheckFailure;
import sonia.scm.repository.Permission;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryPermissions;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
@SuppressWarnings("squid:S3306")
@@ -17,4 +24,16 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
abstract HealthCheckFailureDto toDto(HealthCheckFailure failure);
abstract PermissionDto toDto(Permission permission);
@AfterMapping
void appendLinks(Repository repository, @MappingTarget RepositoryDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(target.getNamespace(), target.getName()));
if (RepositoryPermissions.delete(repository).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.repository().delete(target.getNamespace(), target.getName())));
}
if (RepositoryPermissions.modify(repository).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.repository().update(target.getNamespace(), target.getName())));
}
target.add(linksBuilder.build());
}
}

View File

@@ -100,4 +100,28 @@ class ResourceLinks {
return collectionLinkBuilder.method("getUserCollectionResource").parameters().method("create").parameters().href();
}
}
public RepositoryLinks repository() {
return null;
}
static class RepositoryLinks {
private final LinkBuilder repositoryLinkBuilder;
private RepositoryLinks(UriInfo uriInfo) {
repositoryLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class);
}
String self(String namespace, String name) {
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("get").parameters().href();
}
String delete(String namespace, String name) {
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("delete").parameters().href();
}
String update(String namespace, String name) {
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("update").parameters().href();
}
}
}

View File

@@ -1,10 +1,13 @@
package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Answers;
import org.mockito.InjectMocks;
@@ -22,10 +25,18 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@SubjectAware(
username = "trillian",
password = "secret",
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
public class RepositoryRootResourceTest {
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
@Rule
public ShiroRule shiro = new ShiroRule();
@Mock
private RepositoryManager repositoryManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -71,6 +82,7 @@ public class RepositoryRootResourceTest {
Repository repository = new Repository();
repository.setNamespace(namespace);
repository.setName(name);
repository.setId("id");
when(repositoryManager.getByNamespace(namespace, name)).thenReturn(repository);
return repository;
}

View File

@@ -1,48 +1,110 @@
package sonia.scm.api.v2.resources;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.repository.HealthCheckFailure;
import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
import java.net.URI;
import java.net.URISyntaxException;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
public class RepositoryToRepositoryDtoMapperTest {
private RepositoryToRepositoryDtoMapperImpl mapper = new RepositoryToRepositoryDtoMapperImpl();
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ResourceLinks resourceLinks;
@InjectMocks
private RepositoryToRepositoryDtoMapperImpl mapper;
private final Subject subject = mock(Subject.class);
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
private URI expectedBaseUri;
@Before
public void init() throws URISyntaxException {
initMocks(this);
URI baseUri = new URI("http://example.com/base/");
expectedBaseUri = baseUri.resolve(RepositoryRootResource.REPOSITORIES_PATH_V2 + "/");
subjectThreadState.bind();
ResourceLinksMock.initMock(resourceLinks, baseUri);
ThreadContext.bind(subject);
}
@Test
public void shouldMapSimpleProperties() {
RepositoryDto dto = mapper.map(createDummyRepository());
assertEquals("namespace", dto.getNamespace());
assertEquals("name", dto.getName());
RepositoryDto dto = mapper.map(createTestRepository());
assertEquals("testspace", dto.getNamespace());
assertEquals("test", dto.getName());
assertEquals("description", dto.getDescription());
assertEquals("git", dto.getType());
assertEquals("none@example.com", dto.getContact());
assertEquals("1", dto.getId());
}
@Test
public void shouldCreateLinksForUnprivilegedUser() {
RepositoryDto dto = mapper.map(createTestRepository());
assertEquals(
"http://example.com/base/v2/groups/testspace/test",
dto.getLinks().getLinkBy("self").get().getHref());
assertFalse(dto.getLinks().getLinkBy("update").isPresent());
assertFalse(dto.getLinks().getLinkBy("delete").isPresent());
}
@Test
public void shouldCreateDeleteLink() {
when(subject.isPermitted("repository:delete:1")).thenReturn(true);
RepositoryDto dto = mapper.map(createTestRepository());
assertEquals(
"http://example.com/base/v2/groups/testspace/test",
dto.getLinks().getLinkBy("delete").get().getHref());
}
@Test
public void shouldCreateUpdateLink() {
when(subject.isPermitted("repository:modify:1")).thenReturn(true);
RepositoryDto dto = mapper.map(createTestRepository());
assertEquals(
"http://example.com/base/v2/groups/testspace/test",
dto.getLinks().getLinkBy("update").get().getHref());
}
@Test
public void shouldMapHealthCheck() {
RepositoryDto dto = mapper.map(createDummyRepository());
RepositoryDto dto = mapper.map(createTestRepository());
assertEquals(1, dto.getHealthCheckFailures().size());
assertEquals("summary", dto.getHealthCheckFailures().get(0).getSummary());
}
@Test
public void shouldMapPermissions() {
RepositoryDto dto = mapper.map(createDummyRepository());
RepositoryDto dto = mapper.map(createTestRepository());
assertEquals(1, dto.getPermissions().size());
assertEquals("permission", dto.getPermissions().get(0).getName());
assertEquals("READ", dto.getPermissions().get(0).getType());
}
private Repository createDummyRepository() {
private Repository createTestRepository() {
Repository repository = new Repository();
repository.setNamespace("namespace");
repository.setName("name");
repository.setNamespace("testspace");
repository.setName("test");
repository.setDescription("description");
repository.setType("git");
repository.setContact("none@example.com");

View File

@@ -22,5 +22,9 @@ public class ResourceLinksMock {
when(resourceLinks.groupCollection().self()).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2);
when(resourceLinks.groupCollection().create()).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2);
when(resourceLinks.repository().self(anyString(), anyString())).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2 + invocation.getArguments()[0] + "/" + invocation.getArguments()[1]);
when(resourceLinks.repository().update(anyString(), anyString())).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2 + invocation.getArguments()[0] + "/" + invocation.getArguments()[1]);
when(resourceLinks.repository().delete(anyString(), anyString())).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2 + invocation.getArguments()[0] + "/" + invocation.getArguments()[1]);
}
}