mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
merge
This commit is contained in:
@@ -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<E extends ModelObject, D extends HalRepresentation, M extends BaseMapper<E, D>> {
|
||||
|
||||
private final String collectionName;
|
||||
public class BasicCollectionToDtoMapper<E extends ModelObject, D extends HalRepresentation, M extends BaseMapper<E, D>> extends PagedCollectionToDtoMapper<E, D> {
|
||||
|
||||
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<E> pageResult, String selfLink, Optional<String> createLink) {
|
||||
return map(pageNumber, pageSize, pageResult, selfLink, createLink, entityToDtoMapper::map);
|
||||
}
|
||||
|
||||
CollectionDto map(int pageNumber, int pageSize, PageResult<E> pageResult, String selfLink, Optional<String> createLink, Function<E, ? extends HalRepresentation> mapper) {
|
||||
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount());
|
||||
List<HalRepresentation> 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<E> pageResult) {
|
||||
if (pageResult.getOverallCount() % pageSize > 0) {
|
||||
return pageResult.getOverallCount() / pageSize + 1;
|
||||
} else {
|
||||
return pageResult.getOverallCount() / pageSize;
|
||||
}
|
||||
}
|
||||
|
||||
private Links createLinks(NumberedPaging page, String selfLink, Optional<String> 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<HalRepresentation> dtos) {
|
||||
return embeddedBuilder()
|
||||
.with(collectionName, dtos)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ChangesetCollectionToDtoMapper extends BasicCollectionToDtoMapper<Changeset, ChangesetDto, ChangesetToChangesetDtoMapper> {
|
||||
public class ChangesetCollectionToDtoMapper extends PagedCollectionToDtoMapper<Changeset, ChangesetDto> {
|
||||
|
||||
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<C
|
||||
return resourceLinks.changeset().all(repository.getNamespace(), repository.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import static de.otto.edison.hal.Link.link;
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
@Mapper
|
||||
public abstract class ChangesetToChangesetDtoMapper extends BaseMapper<Changeset, ChangesetDto> {
|
||||
public abstract class ChangesetToChangesetDtoMapper implements InstantAttributeMapper {
|
||||
|
||||
@Inject
|
||||
private RepositoryServiceFactory serviceFactory;
|
||||
@@ -65,7 +65,8 @@ public abstract class ChangesetToChangesetDtoMapper extends BaseMapper<Changeset
|
||||
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId()))
|
||||
.single(link("diff", resourceLinks.diff().self(namespace, name, target.getId())));
|
||||
.single(link("diff", resourceLinks.diff().self(namespace, name, target.getId())))
|
||||
.single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId())));
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Changeset, ParentChangesetDto> {
|
||||
public abstract class ChangesetToParentDtoMapper {
|
||||
|
||||
@Inject
|
||||
private ResourceLinks resourceLinks;
|
||||
|
||||
@@ -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<FileObject, FileObjectDto> {
|
||||
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);
|
||||
|
||||
@@ -34,6 +34,7 @@ public class MapperModule extends AbstractModule {
|
||||
bind(TagToTagDtoMapper.class).to(Mappers.getMapper(TagToTagDtoMapper.class).getClass());
|
||||
|
||||
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
|
||||
bind(ModificationsToDtoMapper.class).to(Mappers.getMapper(ModificationsToDtoMapper.class).getClass());
|
||||
|
||||
// no mapstruct required
|
||||
bind(UIPluginDtoMapper.class);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ModificationsDto extends HalRepresentation {
|
||||
|
||||
|
||||
private String revision;
|
||||
/**
|
||||
* list of added files
|
||||
*/
|
||||
private List<String> added;
|
||||
|
||||
/**
|
||||
* list of modified files
|
||||
*/
|
||||
private List<String> modified;
|
||||
|
||||
/**
|
||||
* list of removed files
|
||||
*/
|
||||
private List<String> removed;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||
protected HalRepresentation add(Links links) {
|
||||
return super.add(links);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.RepositoryNotFoundException;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ModificationsRootResource {
|
||||
|
||||
private final RepositoryServiceFactory serviceFactory;
|
||||
private final ModificationsToDtoMapper modificationsToDtoMapper;
|
||||
|
||||
@Inject
|
||||
public ModificationsRootResource(RepositoryServiceFactory serviceFactory, ModificationsToDtoMapper modificationsToDtoMapper) {
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.modificationsToDtoMapper = modificationsToDtoMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file modifications related to a revision.
|
||||
* file modifications are for example: Modified, Added or Removed.
|
||||
*/
|
||||
@GET
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the modifications"),
|
||||
@ResponseCode(code = 404, condition = "not found, no changeset with the specified id is available in the repository"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@Produces(VndMediaType.MODIFICATIONS)
|
||||
@TypeHint(ModificationsDto.class)
|
||||
@Path("{revision}")
|
||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException, RevisionNotFoundException, RepositoryNotFoundException , InternalRepositoryException {
|
||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||
Modifications modifications = repositoryService.getModificationsCommand()
|
||||
.revision(revision)
|
||||
.getModifications();
|
||||
ModificationsDto output = modificationsToDtoMapper.map(modifications, repositoryService.getRepository());
|
||||
if (modifications != null ) {
|
||||
return Response.ok(output).build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
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.Modifications;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
@Mapper
|
||||
public abstract class ModificationsToDtoMapper {
|
||||
|
||||
@Inject
|
||||
private ResourceLinks resourceLinks;
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
public abstract ModificationsDto map(Modifications modifications, @Context Repository repository);
|
||||
|
||||
@AfterMapping
|
||||
void appendLinks(@MappingTarget ModificationsDto target, @Context Repository repository) {
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.self(resourceLinks.modifications().self(repository.getNamespace(), repository.getName(), target.getRevision()));
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
}
|
||||
@@ -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<E extends ModelObject, D extends HalRepresentation> {
|
||||
|
||||
private final String collectionName;
|
||||
|
||||
PagedCollectionToDtoMapper(String collectionName) {
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
CollectionDto map(int pageNumber, int pageSize, PageResult<E> pageResult, String selfLink, Optional<String> createLink, Function<E, ? extends HalRepresentation> mapper) {
|
||||
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount());
|
||||
List<HalRepresentation> 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<E> pageResult) {
|
||||
if (pageResult.getOverallCount() % pageSize > 0) {
|
||||
return pageResult.getOverallCount() / pageSize + 1;
|
||||
} else {
|
||||
return pageResult.getOverallCount() / pageSize;
|
||||
}
|
||||
}
|
||||
|
||||
private Links createLinks(NumberedPaging page, String selfLink, Optional<String> 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<HalRepresentation> dtos) {
|
||||
return embeddedBuilder()
|
||||
.with(collectionName, dtos)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -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<Repository, RepositoryDto, RepositoryToRepositoryDtoMapper> {
|
||||
|
||||
private final ResourceLinks resourceLinks;
|
||||
|
||||
@@ -40,6 +40,7 @@ public class RepositoryResource {
|
||||
private final Provider<ContentResource> contentResource;
|
||||
private final Provider<PermissionRootResource> permissionRootResource;
|
||||
private final Provider<DiffRootResource> diffRootResource;
|
||||
private final Provider<ModificationsRootResource> modificationsRootResource;
|
||||
private final Provider<FileHistoryRootResource> fileHistoryRootResource;
|
||||
|
||||
@Inject
|
||||
@@ -52,7 +53,9 @@ public class RepositoryResource {
|
||||
Provider<SourceRootResource> sourceRootResource, Provider<ContentResource> contentResource,
|
||||
Provider<PermissionRootResource> permissionRootResource,
|
||||
Provider<DiffRootResource> diffRootResource,
|
||||
Provider<FileHistoryRootResource> fileHistoryRootResource) {
|
||||
Provider<ModificationsRootResource> modificationsRootResource,
|
||||
Provider<FileHistoryRootResource> fileHistoryRootResource
|
||||
) {
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.manager = manager;
|
||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||
@@ -64,6 +67,7 @@ public class RepositoryResource {
|
||||
this.contentResource = contentResource;
|
||||
this.permissionRootResource = permissionRootResource;
|
||||
this.diffRootResource = diffRootResource;
|
||||
this.modificationsRootResource = modificationsRootResource;
|
||||
this.fileHistoryRootResource = fileHistoryRootResource;
|
||||
}
|
||||
|
||||
@@ -188,6 +192,9 @@ public class RepositoryResource {
|
||||
return permissionRootResource.get();
|
||||
}
|
||||
|
||||
@Path("modifications/")
|
||||
public ModificationsRootResource modifications() {return modificationsRootResource.get(); }
|
||||
|
||||
private Optional<Response> handleNotArchived(Throwable throwable) {
|
||||
if (throwable instanceof RepositoryIsNotArchivedException) {
|
||||
return Optional.of(Response.status(Response.Status.PRECONDITION_FAILED).build());
|
||||
|
||||
@@ -315,6 +315,21 @@ class ResourceLinks {
|
||||
}
|
||||
}
|
||||
|
||||
public ModificationsLinks modifications() {
|
||||
return new ModificationsLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class ModificationsLinks {
|
||||
private final LinkBuilder modificationsLinkBuilder;
|
||||
|
||||
ModificationsLinks(UriInfo uriInfo) {
|
||||
modificationsLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ModificationsRootResource.class);
|
||||
}
|
||||
String self(String namespace, String name, String revision) {
|
||||
return modificationsLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("modifications").parameters().method("get").parameters(revision).href();
|
||||
}
|
||||
}
|
||||
|
||||
public FileHistoryLinks fileHistory() {
|
||||
return new FileHistoryLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
@@ -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<User, UserDto, UserToUserDtoMapper> {
|
||||
|
||||
private final ResourceLinks resourceLinks;
|
||||
|
||||
Reference in New Issue
Block a user