Extract base class for collection mappers

This commit is contained in:
René Pfeuffer
2018-06-25 09:57:59 +02:00
parent f9a094e1b9
commit 2d32b24e48
10 changed files with 128 additions and 136 deletions

View File

@@ -1,11 +1,15 @@
package sonia.scm.api.v2.resources; package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
import sonia.scm.ModelObject;
import sonia.scm.util.AssertUtil; import sonia.scm.util.AssertUtil;
import java.time.Instant; import java.time.Instant;
import java.util.Optional; import java.util.Optional;
class BaseMapper { abstract class BaseMapper<T extends ModelObject, D extends HalRepresentation> {
public abstract D map(T user);
Instant mapTime(Long epochMilli) { Instant mapTime(Long epochMilli) {
AssertUtil.assertIsNotNull(epochMilli); AssertUtil.assertIsNotNull(epochMilli);

View File

@@ -0,0 +1,69 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import de.otto.edison.hal.paging.NumberedPaging;
import de.otto.edison.hal.paging.PagingRel;
import sonia.scm.ModelObject;
import sonia.scm.PageResult;
import javax.inject.Inject;
import java.util.EnumSet;
import java.util.List;
import static com.damnhandy.uri.template.UriTemplate.fromTemplate;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
import static java.util.stream.Collectors.toList;
abstract class BasicCollectionToDtoMapper<E extends ModelObject, D extends HalRepresentation> {
private final String collectionName;
private final BaseMapper<E, D> entityToDtoMapper;
@Inject
public BasicCollectionToDtoMapper(String collectionName, BaseMapper<E, D> entityToDtoMapper) {
this.collectionName = collectionName;
this.entityToDtoMapper = entityToDtoMapper;
}
public CollectionDto map(int pageNumber, int pageSize, PageResult<E> pageResult) {
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount());
List<D> dtos = pageResult.getEntities().stream().map(entityToDtoMapper::map).collect(toList());
CollectionDto collectionDto = new CollectionDto(
createLinks(paging),
embedDtos(dtos)
);
collectionDto.setPage(pageNumber);
return collectionDto;
}
private Links createLinks(NumberedPaging page) {
String baseUrl = createSelfLink();
Links.Builder linksBuilder = linkingTo()
.with(page.links(
fromTemplate(baseUrl + "{?page,pageSize}"),
EnumSet.allOf(PagingRel.class)));
if (isCreatePermitted()) {
linksBuilder.single(link("create", createCreateLink()));
}
return linksBuilder.build();
}
abstract boolean isCreatePermitted();
abstract String createCreateLink();
abstract String createSelfLink();
private Embedded embedDtos(List<D> dtos) {
return embeddedBuilder()
.with(collectionName, dtos)
.build();
}
}

View File

@@ -6,12 +6,12 @@ import de.otto.edison.hal.Links;
import lombok.Data; import lombok.Data;
@Data @Data
public class UserCollectionDto extends HalRepresentation { public class CollectionDto extends HalRepresentation {
private int page; private int page;
private int pageTotal; private int pageTotal;
public UserCollectionDto(Links links, Embedded embedded) { public CollectionDto(Links links, Embedded embedded) {
super(links, embedded); super(links, embedded);
} }
} }

View File

@@ -1,17 +0,0 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Data;
@Data
public class GroupCollectionDto extends HalRepresentation {
private int page;
private int pageTotal;
public GroupCollectionDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,64 +1,34 @@
package sonia.scm.api.v2.resources; package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links;
import de.otto.edison.hal.paging.NumberedPaging;
import de.otto.edison.hal.paging.PagingRel;
import sonia.scm.PageResult;
import sonia.scm.group.Group; import sonia.scm.group.Group;
import sonia.scm.group.GroupPermissions; import sonia.scm.group.GroupPermissions;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.EnumSet;
import java.util.List;
import static com.damnhandy.uri.template.UriTemplate.fromTemplate;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
import static java.util.stream.Collectors.toList;
import static sonia.scm.api.v2.resources.ResourceLinks.groupCollection; import static sonia.scm.api.v2.resources.ResourceLinks.groupCollection;
public class GroupCollectionToDtoMapper { public class GroupCollectionToDtoMapper extends BasicCollectionToDtoMapper<Group, GroupDto> {
private final GroupToGroupDtoMapper groupToDtoMapper;
private final UriInfoStore uriInfoStore; private final UriInfoStore uriInfoStore;
@Inject @Inject
public GroupCollectionToDtoMapper(GroupToGroupDtoMapper groupToDtoMapper, UriInfoStore uriInfoStore) { public GroupCollectionToDtoMapper(GroupToGroupDtoMapper groupToDtoMapper, UriInfoStore uriInfoStore) {
this.groupToDtoMapper = groupToDtoMapper; super("groups", groupToDtoMapper);
this.uriInfoStore = uriInfoStore; this.uriInfoStore = uriInfoStore;
} }
public GroupCollectionDto map(int pageNumber, int pageSize, PageResult<Group> pageResult) { @Override
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount()); String createCreateLink() {
List<GroupDto> dtos = pageResult.getEntities().stream().map(groupToDtoMapper::map).collect(toList()); return groupCollection(uriInfoStore.get()).create();
GroupCollectionDto groupCollectionDto = new GroupCollectionDto(
createLinks(paging),
embedDtos(dtos)
);
groupCollectionDto.setPage(pageNumber);
return groupCollectionDto;
} }
private Links createLinks(NumberedPaging page) { @Override
String baseUrl = groupCollection(uriInfoStore.get()).self(); String createSelfLink() {
return groupCollection(uriInfoStore.get()).self();
Links.Builder linksBuilder = linkingTo()
.with(page.links(
fromTemplate(baseUrl + "{?page,pageSize}"),
EnumSet.allOf(PagingRel.class)));
if (GroupPermissions.create().isPermitted()) {
linksBuilder.single(link("create", groupCollection(uriInfoStore.get()).create()));
}
return linksBuilder.build();
} }
private Embedded embedDtos(List<GroupDto> dtos) { @Override
return embeddedBuilder() boolean isCreatePermitted() {
.with("groups", dtos) return GroupPermissions.create().isPermitted();
.build();
} }
} }

View File

@@ -17,13 +17,11 @@ import static sonia.scm.api.v2.resources.ResourceLinks.group;
import static sonia.scm.api.v2.resources.ResourceLinks.user; import static sonia.scm.api.v2.resources.ResourceLinks.user;
@Mapper @Mapper
public abstract class GroupToGroupDtoMapper extends BaseMapper { public abstract class GroupToGroupDtoMapper extends BaseMapper<Group, GroupDto> {
@Inject @Inject
private UriInfoStore uriInfoStore; private UriInfoStore uriInfoStore;
public abstract GroupDto map(Group group);
@AfterMapping @AfterMapping
void appendLinks(Group group, @MappingTarget GroupDto target) { void appendLinks(Group group, @MappingTarget GroupDto target) {
Links.Builder linksBuilder = linkingTo().self(group(uriInfoStore.get()).self(target.getName())); Links.Builder linksBuilder = linkingTo().self(group(uriInfoStore.get()).self(target.getName()));

View File

@@ -1,64 +1,34 @@
package sonia.scm.api.v2.resources; package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links;
import de.otto.edison.hal.paging.NumberedPaging;
import de.otto.edison.hal.paging.PagingRel;
import sonia.scm.PageResult;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserPermissions; import sonia.scm.user.UserPermissions;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.EnumSet;
import java.util.List;
import static com.damnhandy.uri.template.UriTemplate.fromTemplate;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
import static java.util.stream.Collectors.toList;
import static sonia.scm.api.v2.resources.ResourceLinks.userCollection; import static sonia.scm.api.v2.resources.ResourceLinks.userCollection;
public class UserCollectionToDtoMapper { public class UserCollectionToDtoMapper extends BasicCollectionToDtoMapper<User, UserDto> {
private final UserToUserDtoMapper userToDtoMapper;
private final UriInfoStore uriInfoStore; private final UriInfoStore uriInfoStore;
@Inject @Inject
public UserCollectionToDtoMapper(UserToUserDtoMapper userToDtoMapper, UriInfoStore uriInfoStore) { public UserCollectionToDtoMapper(UserToUserDtoMapper userToDtoMapper, UriInfoStore uriInfoStore) {
this.userToDtoMapper = userToDtoMapper; super("users", userToDtoMapper);
this.uriInfoStore = uriInfoStore; this.uriInfoStore = uriInfoStore;
} }
public UserCollectionDto map(int pageNumber, int pageSize, PageResult<User> pageResult) { @Override
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount()); String createCreateLink() {
List<UserDto> dtos = pageResult.getEntities().stream().map(userToDtoMapper::map).collect(toList()); return userCollection(uriInfoStore.get()).create();
UserCollectionDto userCollectionDto = new UserCollectionDto(
createLinks(paging),
embedDtos(dtos)
);
userCollectionDto.setPage(pageNumber);
return userCollectionDto;
} }
private Links createLinks(NumberedPaging page) { @Override
String baseUrl = userCollection(uriInfoStore.get()).self(); String createSelfLink() {
return userCollection(uriInfoStore.get()).self();
Links.Builder linksBuilder = linkingTo()
.with(page.links(
fromTemplate(baseUrl + "{?page,pageSize}"),
EnumSet.allOf(PagingRel.class)));
if (UserPermissions.create().isPermitted()) {
linksBuilder.single(link("create", userCollection(uriInfoStore.get()).create()));
}
return linksBuilder.build();
} }
private Embedded embedDtos(List<UserDto> dtos) { @Override
return embeddedBuilder() boolean isCreatePermitted() {
.with("users", dtos) return UserPermissions.create().isPermitted();
.build();
} }
} }

View File

@@ -15,13 +15,11 @@ import static de.otto.edison.hal.Links.linkingTo;
import static sonia.scm.api.v2.resources.ResourceLinks.user; import static sonia.scm.api.v2.resources.ResourceLinks.user;
@Mapper @Mapper
public abstract class UserToUserDtoMapper extends BaseMapper { public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
@Inject @Inject
private UriInfoStore uriInfoStore; private UriInfoStore uriInfoStore;
public abstract UserDto map(User user);
@AfterMapping @AfterMapping
void removePassword(@MappingTarget UserDto target) { void removePassword(@MappingTarget UserDto target) {
target.setPassword(UserResource.DUMMY_PASSWORT); target.setPassword(UserResource.DUMMY_PASSWORT);

View File

@@ -56,29 +56,29 @@ public class GroupCollectionToDtoMapperTest {
@Test @Test
public void shouldSetPageNumber() { public void shouldSetPageNumber() {
PageResult<Group> pageResult = mockPageResult("nobodies"); PageResult<Group> pageResult = mockPageResult("nobodies");
GroupCollectionDto groupCollectionDto = mapper.map(1, 1, pageResult); CollectionDto collectionDto = mapper.map(1, 1, pageResult);
assertEquals(1, groupCollectionDto.getPage()); assertEquals(1, collectionDto.getPage());
} }
@Test @Test
public void shouldHaveSelfLink() { public void shouldHaveSelfLink() {
PageResult<Group> pageResult = mockPageResult("nobodies"); PageResult<Group> pageResult = mockPageResult("nobodies");
GroupCollectionDto groupCollectionDto = mapper.map(1, 1, pageResult); CollectionDto collectionDto = mapper.map(1, 1, pageResult);
assertTrue(groupCollectionDto.getLinks().getLinkBy("self").get().getHref().startsWith(expectedBaseUri.toString())); assertTrue(collectionDto.getLinks().getLinkBy("self").get().getHref().startsWith(expectedBaseUri.toString()));
} }
@Test @Test
public void shouldCreateNextPageLink_whenHasMore() { public void shouldCreateNextPageLink_whenHasMore() {
PageResult<Group> pageResult = createPage(createGroups("nobodies", "bosses"), 0, 1); PageResult<Group> pageResult = createPage(createGroups("nobodies", "bosses"), 0, 1);
GroupCollectionDto groupCollectionDto = mapper.map(0, 1, pageResult); CollectionDto collectionDto = mapper.map(0, 1, pageResult);
assertTrue(groupCollectionDto.getLinks().getLinkBy("next").get().getHref().contains("page=1")); assertTrue(collectionDto.getLinks().getLinkBy("next").get().getHref().contains("page=1"));
} }
@Test @Test
public void shouldNotCreateNextPageLink_whenNoMore() { public void shouldNotCreateNextPageLink_whenNoMore() {
PageResult<Group> pageResult = mockPageResult("nobodies"); PageResult<Group> pageResult = mockPageResult("nobodies");
GroupCollectionDto groupCollectionDto = mapper.map(1, 1, pageResult); CollectionDto collectionDto = mapper.map(1, 1, pageResult);
assertFalse(groupCollectionDto.getLinks().stream().anyMatch(link -> link.getHref().contains("page=2"))); assertFalse(collectionDto.getLinks().stream().anyMatch(link -> link.getHref().contains("page=2")));
} }
@Test @Test
@@ -86,9 +86,9 @@ public class GroupCollectionToDtoMapperTest {
PageResult<Group> pageResult = mockPageResult("nobodies"); PageResult<Group> pageResult = mockPageResult("nobodies");
when(subject.isPermitted("group:create")).thenReturn(true); when(subject.isPermitted("group:create")).thenReturn(true);
GroupCollectionDto groupCollectionDto = mapper.map(1, 1, pageResult); CollectionDto collectionDto = mapper.map(1, 1, pageResult);
assertTrue(groupCollectionDto.getLinks().getLinkBy("create").isPresent()); assertTrue(collectionDto.getLinks().getLinkBy("create").isPresent());
} }
@Test @Test
@@ -96,16 +96,16 @@ public class GroupCollectionToDtoMapperTest {
PageResult<Group> pageResult = mockPageResult("nobodies"); PageResult<Group> pageResult = mockPageResult("nobodies");
when(subject.isPermitted("group:create")).thenReturn(false); when(subject.isPermitted("group:create")).thenReturn(false);
GroupCollectionDto groupCollectionDto = mapper.map(1, 1, pageResult); CollectionDto collectionDto = mapper.map(1, 1, pageResult);
assertFalse(groupCollectionDto.getLinks().getLinkBy("create").isPresent()); assertFalse(collectionDto.getLinks().getLinkBy("create").isPresent());
} }
@Test @Test
public void shouldMapGroups() { public void shouldMapGroups() {
PageResult<Group> pageResult = mockPageResult("nobodies", "bosses"); PageResult<Group> pageResult = mockPageResult("nobodies", "bosses");
GroupCollectionDto groupCollectionDto = mapper.map(1, 2, pageResult); CollectionDto collectionDto = mapper.map(1, 2, pageResult);
List<HalRepresentation> groups = groupCollectionDto.getEmbedded().getItemsBy("groups"); List<HalRepresentation> groups = collectionDto.getEmbedded().getItemsBy("groups");
assertEquals(2, groups.size()); assertEquals(2, groups.size());
assertEquals("nobodies", ((GroupDto) groups.get(0)).getName()); assertEquals("nobodies", ((GroupDto) groups.get(0)).getName());
assertEquals("bosses", ((GroupDto) groups.get(1)).getName()); assertEquals("bosses", ((GroupDto) groups.get(1)).getName());

View File

@@ -63,30 +63,30 @@ public class UserCollectionToDtoMapperTest {
@Test @Test
public void shouldSetPageNumber() { public void shouldSetPageNumber() {
PageResult<User> pageResult = mockPageResult("Hannes"); PageResult<User> pageResult = mockPageResult("Hannes");
UserCollectionDto userCollectionDto = mapper.map(0, 1, pageResult); CollectionDto collectionDto = mapper.map(0, 1, pageResult);
assertEquals(0, userCollectionDto.getPage()); assertEquals(0, collectionDto.getPage());
} }
@Test @Test
public void shouldHaveSelfLink() { public void shouldHaveSelfLink() {
PageResult<User> pageResult = mockPageResult("Hannes"); PageResult<User> pageResult = mockPageResult("Hannes");
UserCollectionDto userCollectionDto = mapper.map(0, 1, pageResult); CollectionDto collectionDto = mapper.map(0, 1, pageResult);
assertTrue(userCollectionDto.getLinks().getLinkBy("self").get().getHref().startsWith(expectedBaseUri.toString())); assertTrue(collectionDto.getLinks().getLinkBy("self").get().getHref().startsWith(expectedBaseUri.toString()));
} }
@Test @Test
public void shouldCreateNextPageLink_whenHasMore() { public void shouldCreateNextPageLink_whenHasMore() {
PageResult<User> pageResult = createPage(createUsers("Hannes", "Karl"), 0, 1); PageResult<User> pageResult = createPage(createUsers("Hannes", "Karl"), 0, 1);
UserCollectionDto userCollectionDto = mapper.map(0, 1, pageResult); CollectionDto collectionDto = mapper.map(0, 1, pageResult);
assertTrue(userCollectionDto.getLinks().getLinkBy("next").get().getHref().contains("page=1")); assertTrue(collectionDto.getLinks().getLinkBy("next").get().getHref().contains("page=1"));
} }
@Test @Test
public void shouldNotCreateNextPageLink_whenNoMore() { public void shouldNotCreateNextPageLink_whenNoMore() {
PageResult<User> pageResult = mockPageResult("Hannes", "Wurst", "X"); PageResult<User> pageResult = mockPageResult("Hannes", "Wurst", "X");
UserCollectionDto userCollectionDto = mapper.map(0, 10, pageResult); CollectionDto collectionDto = mapper.map(0, 10, pageResult);
assertFalse(userCollectionDto.getLinks().getLinkBy("next").isPresent()); assertFalse(collectionDto.getLinks().getLinkBy("next").isPresent());
} }
@Test @Test
@@ -94,9 +94,9 @@ public class UserCollectionToDtoMapperTest {
PageResult<User> pageResult = mockPageResult("Hannes"); PageResult<User> pageResult = mockPageResult("Hannes");
when(subject.isPermitted("user:create")).thenReturn(true); when(subject.isPermitted("user:create")).thenReturn(true);
UserCollectionDto userCollectionDto = mapper.map(0, 1, pageResult); CollectionDto collectionDto = mapper.map(0, 1, pageResult);
assertTrue(userCollectionDto.getLinks().getLinkBy("create").isPresent()); assertTrue(collectionDto.getLinks().getLinkBy("create").isPresent());
} }
@Test @Test
@@ -104,16 +104,16 @@ public class UserCollectionToDtoMapperTest {
PageResult<User> pageResult = mockPageResult("Hannes"); PageResult<User> pageResult = mockPageResult("Hannes");
when(subject.isPermitted("user:create")).thenReturn(false); when(subject.isPermitted("user:create")).thenReturn(false);
UserCollectionDto userCollectionDto = mapper.map(0, 1, pageResult); CollectionDto collectionDto = mapper.map(0, 1, pageResult);
assertFalse(userCollectionDto.getLinks().getLinkBy("create").isPresent()); assertFalse(collectionDto.getLinks().getLinkBy("create").isPresent());
} }
@Test @Test
public void shouldMapUsers() { public void shouldMapUsers() {
PageResult<User> pageResult = mockPageResult("Hannes", "Wurst"); PageResult<User> pageResult = mockPageResult("Hannes", "Wurst");
UserCollectionDto userCollectionDto = mapper.map(0, 2, pageResult); CollectionDto collectionDto = mapper.map(0, 2, pageResult);
List<HalRepresentation> users = userCollectionDto.getEmbedded().getItemsBy("users"); List<HalRepresentation> users = collectionDto.getEmbedded().getItemsBy("users");
assertEquals(2, users.size()); assertEquals(2, users.size());
assertEquals("Hannes", ((UserDto) users.get(0)).getName()); assertEquals("Hannes", ((UserDto) users.get(0)).getName());
assertEquals("Wurst", ((UserDto) users.get(1)).getName()); assertEquals("Wurst", ((UserDto) users.get(1)).getName());