From 7372f7a2ab940fc45fb6b3ed52565c1925e018df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 14 Sep 2018 15:00:09 +0200 Subject: [PATCH] Introduce interface to be used by dto mappers with Instant fields --- .../scm/api/v2/resources/BaseMapper.java | 8 +-- .../v2/resources/InstantAttributeMapper.java | 9 +++ .../resources/BasicCollectionToDtoMapper.java | 56 +--------------- .../ChangesetCollectionToDtoMapper.java | 5 +- .../ChangesetToChangesetDtoMapper.java | 2 +- .../resources/ChangesetToParentDtoMapper.java | 2 +- .../FileObjectToFileObjectDtoMapper.java | 4 +- .../resources/PagedCollectionToDtoMapper.java | 64 +++++++++++++++++++ .../RepositoryCollectionToDtoMapper.java | 2 - .../resources/UserCollectionToDtoMapper.java | 2 - 10 files changed, 84 insertions(+), 70 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/api/v2/resources/InstantAttributeMapper.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/PagedCollectionToDtoMapper.java diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java index e4cf8ecb5d..d7f299d989 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java @@ -3,14 +3,8 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; import org.mapstruct.Mapping; -import java.time.Instant; - -public abstract class BaseMapper { +public abstract class BaseMapper implements InstantAttributeMapper { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract D map(T modelObject); - - protected Instant mapTime(Long epochMilli) { - return epochMilli == null? null: Instant.ofEpochMilli(epochMilli); - } } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/InstantAttributeMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/InstantAttributeMapper.java new file mode 100644 index 0000000000..468bdfc137 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/InstantAttributeMapper.java @@ -0,0 +1,9 @@ +package sonia.scm.api.v2.resources; + +import java.time.Instant; + +public interface InstantAttributeMapper { + default Instant mapTime(Long epochMilli) { + return epochMilli == null? null: Instant.ofEpochMilli(epochMilli); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BasicCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BasicCollectionToDtoMapper.java index b189a22d1c..011c78b413 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BasicCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BasicCollectionToDtoMapper.java @@ -1,73 +1,21 @@ 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 java.util.Optional; -import java.util.function.Function; -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> { - - private final String collectionName; +public class BasicCollectionToDtoMapper> extends PagedCollectionToDtoMapper { private final M entityToDtoMapper; - @Inject public BasicCollectionToDtoMapper(String collectionName, M entityToDtoMapper) { - this.collectionName = collectionName; + super(collectionName); this.entityToDtoMapper = entityToDtoMapper; } CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, String selfLink, Optional createLink) { return map(pageNumber, pageSize, pageResult, selfLink, createLink, entityToDtoMapper::map); } - - CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, String selfLink, Optional createLink, Function mapper) { - NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount()); - List dtos = pageResult.getEntities().stream().map(mapper).collect(toList()); - CollectionDto collectionDto = new CollectionDto( - createLinks(paging, selfLink, createLink), - embedDtos(dtos)); - collectionDto.setPage(pageNumber); - collectionDto.setPageTotal(computePageTotal(pageSize, pageResult)); - return collectionDto; - } - - private int computePageTotal(int pageSize, PageResult pageResult) { - if (pageResult.getOverallCount() % pageSize > 0) { - return pageResult.getOverallCount() / pageSize + 1; - } else { - return pageResult.getOverallCount() / pageSize; - } - } - - private Links createLinks(NumberedPaging page, String selfLink, Optional createLink) { - Links.Builder linksBuilder = linkingTo() - .with(page.links( - fromTemplate(selfLink + "{?page,pageSize}"), - EnumSet.allOf(PagingRel.class))); - createLink.ifPresent(link -> linksBuilder.single(link("create", link))); - return linksBuilder.build(); - } - - private Embedded embedDtos(List dtos) { - return embeddedBuilder() - .with(collectionName, dtos) - .build(); - } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java index 3af3a1d15a..2f7ac86e14 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java @@ -8,14 +8,14 @@ import javax.inject.Inject; import java.util.Optional; import java.util.function.Supplier; -public class ChangesetCollectionToDtoMapper extends BasicCollectionToDtoMapper { +public class ChangesetCollectionToDtoMapper extends PagedCollectionToDtoMapper { private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper; protected final ResourceLinks resourceLinks; @Inject public ChangesetCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) { - super("changesets", changesetToChangesetDtoMapper); + super("changesets"); this.changesetToChangesetDtoMapper = changesetToChangesetDtoMapper; this.resourceLinks = resourceLinks; } @@ -32,3 +32,4 @@ public class ChangesetCollectionToDtoMapper extends BasicCollectionToDtoMapper { +public abstract class ChangesetToChangesetDtoMapper implements InstantAttributeMapper { @Inject private RepositoryServiceFactory serviceFactory; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToParentDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToParentDtoMapper.java index 611f5e6cbb..a644058881 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToParentDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToParentDtoMapper.java @@ -16,7 +16,7 @@ import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class ChangesetToParentDtoMapper extends BaseMapper { +public abstract class ChangesetToParentDtoMapper { @Inject private ResourceLinks resourceLinks; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index 01085958f8..365c0ad4cb 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -4,6 +4,7 @@ import de.otto.edison.hal.Links; import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; import sonia.scm.repository.FileObject; import sonia.scm.repository.NamespaceAndName; @@ -14,11 +15,12 @@ import javax.inject.Inject; import static de.otto.edison.hal.Link.link; @Mapper -public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper { +public abstract class FileObjectToFileObjectDtoMapper implements InstantAttributeMapper { @Inject private ResourceLinks resourceLinks; + @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes protected abstract FileObjectDto map(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context String revision); abstract SubRepositoryDto mapSubrepository(SubRepository subRepository); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PagedCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PagedCollectionToDtoMapper.java new file mode 100644 index 0000000000..c05ec0b1f3 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PagedCollectionToDtoMapper.java @@ -0,0 +1,64 @@ +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 java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +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 PagedCollectionToDtoMapper { + + private final String collectionName; + + PagedCollectionToDtoMapper(String collectionName) { + this.collectionName = collectionName; + } + + CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, String selfLink, Optional createLink, Function mapper) { + NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount()); + List dtos = pageResult.getEntities().stream().map(mapper).collect(toList()); + CollectionDto collectionDto = new CollectionDto( + createLinks(paging, selfLink, createLink), + embedDtos(dtos)); + collectionDto.setPage(pageNumber); + collectionDto.setPageTotal(computePageTotal(pageSize, pageResult)); + return collectionDto; + } + + private int computePageTotal(int pageSize, PageResult pageResult) { + if (pageResult.getOverallCount() % pageSize > 0) { + return pageResult.getOverallCount() / pageSize + 1; + } else { + return pageResult.getOverallCount() / pageSize; + } + } + + private Links createLinks(NumberedPaging page, String selfLink, Optional createLink) { + Links.Builder linksBuilder = linkingTo() + .with(page.links( + fromTemplate(selfLink + "{?page,pageSize}"), + EnumSet.allOf(PagingRel.class))); + createLink.ifPresent(link -> linksBuilder.single(link("create", link))); + return linksBuilder.build(); + } + + private Embedded embedDtos(List dtos) { + return embeddedBuilder() + .with(collectionName, dtos) + .build(); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionToDtoMapper.java index 77674270d7..4e52ca29ac 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionToDtoMapper.java @@ -10,8 +10,6 @@ import java.util.Optional; import static java.util.Optional.empty; import static java.util.Optional.of; -// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. -@SuppressWarnings("squid:S3306") public class RepositoryCollectionToDtoMapper extends BasicCollectionToDtoMapper { private final ResourceLinks resourceLinks; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java index c70bd268ff..464da8ee23 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java @@ -10,8 +10,6 @@ import java.util.Optional; import static java.util.Optional.empty; import static java.util.Optional.of; -// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. -@SuppressWarnings("squid:S3306") public class UserCollectionToDtoMapper extends BasicCollectionToDtoMapper { private final ResourceLinks resourceLinks;