mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 07:25:44 +01:00
Merged in feature/merge_endpoint (pull request #122)
Feature merge endpoint
This commit is contained in:
@@ -43,6 +43,8 @@ public class MapperModule extends AbstractModule {
|
||||
bind(ScmViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ScmViolationExceptionToErrorDtoMapper.class).getClass());
|
||||
bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass());
|
||||
|
||||
bind(MergeResultToDtoMapper.class).to(Mappers.getMapper(MergeResultToDtoMapper.class).getClass());
|
||||
|
||||
// no mapstruct required
|
||||
bind(UIPluginDtoMapper.class);
|
||||
bind(UIPluginDtoCollectionMapper.class);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
@Getter @Setter
|
||||
public class MergeCommandDto {
|
||||
|
||||
@NotEmpty
|
||||
private String sourceRevision;
|
||||
@NotEmpty
|
||||
private String targetRevision;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpStatus;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.api.MergeCommandBuilder;
|
||||
import sonia.scm.repository.api.MergeCommandResult;
|
||||
import sonia.scm.repository.api.MergeDryRunCommandResult;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
@Slf4j
|
||||
public class MergeResource {
|
||||
|
||||
private final RepositoryServiceFactory serviceFactory;
|
||||
private final MergeResultToDtoMapper mapper;
|
||||
|
||||
@Inject
|
||||
public MergeResource(RepositoryServiceFactory serviceFactory, MergeResultToDtoMapper mapper) {
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("")
|
||||
@Produces(VndMediaType.MERGE_RESULT)
|
||||
@Consumes(VndMediaType.MERGE_COMMAND)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 204, condition = "merge has been executed successfully"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the privilege to write the repository"),
|
||||
@ResponseCode(code = 409, condition = "The branches could not be merged automatically due to conflicts (conflicting files will be returned)"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response merge(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid MergeCommandDto mergeCommand) {
|
||||
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
|
||||
log.info("Merge in Repository {}/{} from {} to {}", namespace, name, mergeCommand.getSourceRevision(), mergeCommand.getTargetRevision());
|
||||
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
|
||||
MergeCommandResult mergeCommandResult = createMergeCommand(mergeCommand, repositoryService).executeMerge();
|
||||
if (mergeCommandResult.isSuccess()) {
|
||||
return Response.noContent().build();
|
||||
} else {
|
||||
return Response.status(HttpStatus.SC_CONFLICT).entity(mapper.map(mergeCommandResult)).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("dry-run/")
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 204, condition = "merge can be done automatically"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 409, condition = "The branches can not be merged automatically due to conflicts"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response dryRun(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid MergeCommandDto mergeCommand) {
|
||||
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
|
||||
log.info("Merge in Repository {}/{} from {} to {}", namespace, name, mergeCommand.getSourceRevision(), mergeCommand.getTargetRevision());
|
||||
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
|
||||
MergeDryRunCommandResult mergeCommandResult = createMergeCommand(mergeCommand, repositoryService).dryRun();
|
||||
if (mergeCommandResult.isMergeable()) {
|
||||
return Response.noContent().build();
|
||||
} else {
|
||||
return Response.status(HttpStatus.SC_CONFLICT).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MergeCommandBuilder createMergeCommand(MergeCommandDto mergeCommand, RepositoryService repositoryService) {
|
||||
return repositoryService
|
||||
.getMergeCommand()
|
||||
.setBranchToMerge(mergeCommand.getSourceRevision())
|
||||
.setTargetBranch(mergeCommand.getTargetRevision());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class MergeResultDto {
|
||||
private Collection<String> filesWithConflict;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import sonia.scm.repository.api.MergeCommandResult;
|
||||
|
||||
@Mapper
|
||||
public interface MergeResultToDtoMapper {
|
||||
MergeResultDto map(MergeCommandResult result);
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
@@ -44,6 +43,7 @@ public class RepositoryResource {
|
||||
private final Provider<DiffRootResource> diffRootResource;
|
||||
private final Provider<ModificationsRootResource> modificationsRootResource;
|
||||
private final Provider<FileHistoryRootResource> fileHistoryRootResource;
|
||||
private final Provider<MergeResource> mergeResource;
|
||||
|
||||
@Inject
|
||||
public RepositoryResource(
|
||||
@@ -56,8 +56,8 @@ public class RepositoryResource {
|
||||
Provider<PermissionRootResource> permissionRootResource,
|
||||
Provider<DiffRootResource> diffRootResource,
|
||||
Provider<ModificationsRootResource> modificationsRootResource,
|
||||
Provider<FileHistoryRootResource> fileHistoryRootResource
|
||||
) {
|
||||
Provider<FileHistoryRootResource> fileHistoryRootResource,
|
||||
Provider<MergeResource> mergeResource) {
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.manager = manager;
|
||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||
@@ -71,6 +71,7 @@ public class RepositoryResource {
|
||||
this.diffRootResource = diffRootResource;
|
||||
this.modificationsRootResource = modificationsRootResource;
|
||||
this.fileHistoryRootResource = fileHistoryRootResource;
|
||||
this.mergeResource = mergeResource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,9 +195,12 @@ public class RepositoryResource {
|
||||
return permissionRootResource.get();
|
||||
}
|
||||
|
||||
@Path("modifications/")
|
||||
@Path("modifications/")
|
||||
public ModificationsRootResource modifications() {return modificationsRootResource.get(); }
|
||||
|
||||
@Path("merge/")
|
||||
public MergeResource merge() {return mergeResource.get(); }
|
||||
|
||||
private Optional<Response> handleNotArchived(Throwable throwable) {
|
||||
if (throwable instanceof RepositoryIsNotArchivedException) {
|
||||
return Optional.of(Response.status(Response.Status.PRECONDITION_FAILED).build());
|
||||
|
||||
@@ -55,6 +55,10 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
||||
if (repositoryService.isSupported(Command.BRANCHES)) {
|
||||
linksBuilder.single(link("branches", resourceLinks.branchCollection().self(target.getNamespace(), target.getName())));
|
||||
}
|
||||
if (repositoryService.isSupported(Command.MERGE)) {
|
||||
linksBuilder.single(link("merge", resourceLinks.merge().merge(target.getNamespace(), target.getName())));
|
||||
linksBuilder.single(link("mergeDryRun", resourceLinks.merge().dryRun(target.getNamespace(), target.getName())));
|
||||
}
|
||||
}
|
||||
linksBuilder.single(link("changesets", resourceLinks.changeset().all(target.getNamespace(), target.getName())));
|
||||
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName())));
|
||||
|
||||
@@ -541,4 +541,23 @@ class ResourceLinks {
|
||||
}
|
||||
}
|
||||
|
||||
public MergeLinks merge() {
|
||||
return new MergeLinks(scmPathInfoStore.get());
|
||||
}
|
||||
|
||||
static class MergeLinks {
|
||||
private final LinkBuilder mergeLinkBuilder;
|
||||
|
||||
MergeLinks(ScmPathInfo pathInfo) {
|
||||
this.mergeLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class, MergeResource.class);
|
||||
}
|
||||
|
||||
String merge(String namespace, String name) {
|
||||
return mergeLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("merge").parameters().method("merge").parameters().href();
|
||||
}
|
||||
|
||||
String dryRun(String namespace, String name) {
|
||||
return mergeLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("merge").parameters().method("dryRun").parameters().href();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user