Merge with 2.0.0-m3

This commit is contained in:
René Pfeuffer
2018-08-30 11:48:58 +02:00
26 changed files with 789 additions and 49 deletions

View File

@@ -29,7 +29,7 @@ public abstract class BranchToBranchDtoMapper {
.self(resourceLinks.branch().self(namespaceAndName, target.getName()))
.single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, target.getName())).build())
.single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build())
.single(linkBuilder("source", resourceLinks.source().source(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build());
.single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build());
target.add(linksBuilder.build());
}
}

View File

@@ -0,0 +1,40 @@
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.Iterator;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
public class BrowserResultDto extends HalRepresentation implements Iterable<FileObjectDto> {
private String revision;
private String tag;
private String branch;
// REVIEW files nicht embedded?
private List<FileObjectDto> files;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
}
// REVIEW return null?
@Override
public Iterator<FileObjectDto> iterator() {
Iterator<FileObjectDto> it = null;
if (files != null)
{
it = files.iterator();
}
return it;
}
}

View File

@@ -0,0 +1,50 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Links;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
public class BrowserResultToBrowserResultDtoMapper {
@Inject
private FileObjectToFileObjectDtoMapper fileObjectToFileObjectDtoMapper;
@Inject
private ResourceLinks resourceLinks;
public BrowserResultDto map(BrowserResult browserResult, NamespaceAndName namespaceAndName) {
BrowserResultDto browserResultDto = new BrowserResultDto();
browserResultDto.setTag(browserResult.getTag());
browserResultDto.setBranch(browserResult.getBranch());
browserResultDto.setRevision(browserResult.getRevision());
List<FileObjectDto> fileObjectDtoList = new ArrayList<>();
for (FileObject fileObject : browserResult.getFiles()) {
fileObjectDtoList.add(mapFileObject(fileObject, namespaceAndName, browserResult.getRevision()));
}
browserResultDto.setFiles(fileObjectDtoList);
this.addLinks(browserResult, browserResultDto, namespaceAndName);
return browserResultDto;
}
private FileObjectDto mapFileObject(FileObject fileObject, NamespaceAndName namespaceAndName, String revision) {
return fileObjectToFileObjectDtoMapper.map(fileObject, namespaceAndName, revision);
}
private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName) {
if (browserResult.getRevision() == null) {
dto.add(Links.linkingTo().self(resourceLinks.source().selfWithoutRevision(namespaceAndName.getNamespace(), namespaceAndName.getName())).build());
} else {
dto.add(Links.linkingTo().self(resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision())).build());
}
}
}

View File

@@ -60,8 +60,8 @@ public class ContentResource {
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
StreamingOutput stream = createStreamingOutput(namespace, name, revision, path);
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
StreamingOutput stream = createStreamingOutput(namespace, name, revision, path, repositoryService);
Response.ResponseBuilder responseBuilder = Response.ok(stream);
return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder);
} catch (RepositoryNotFoundException e) {
@@ -70,11 +70,14 @@ public class ContentResource {
}
}
private StreamingOutput createStreamingOutput(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path, RepositoryService repositoryService) {
private StreamingOutput createStreamingOutput(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
return os -> {
try {
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
repositoryService.getCatCommand().setRevision(revision).retriveContent(os, path);
os.close();
} catch (RepositoryNotFoundException e) {
LOG.debug("repository {}/{} not found", path, namespace, name, e);
throw new WebApplicationException(Status.NOT_FOUND);
} catch (PathNotFoundException e) {
LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e);
throw new WebApplicationException(Status.NOT_FOUND);
@@ -141,7 +144,9 @@ public class ContentResource {
try {
byte[] buffer = new byte[HEAD_BUFFER_SIZE];
int length = stream.read(buffer);
if (length < buffer.length) {
if (length < 0) { // empty file
return new byte[]{};
} else if (length < buffer.length) {
return Arrays.copyOf(buffer, length);
} else {
return buffer;

View File

@@ -0,0 +1,28 @@
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.time.Instant;
@Getter
@Setter
@NoArgsConstructor
public class FileObjectDto extends HalRepresentation {
private String name;
private String path;
private boolean directory;
private String description;
private int length;
private Instant lastModified;
private SubRepositoryDto subRepository;
@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

@@ -0,0 +1,46 @@
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.MappingTarget;
import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.SubRepository;
import javax.inject.Inject;
import java.net.URI;
@Mapper
public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper<FileObject, FileObjectDto> {
@Inject
private ResourceLinks resourceLinks;
protected abstract FileObjectDto map(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context String revision);
abstract SubRepositoryDto mapSubrepository(SubRepository subRepository);
@AfterMapping
void addLinks(FileObject fileObject, @MappingTarget FileObjectDto dto, @Context NamespaceAndName namespaceAndName, @Context String revision) {
String path = removeFirstSlash(fileObject.getPath());
Links.Builder links = Links.linkingTo();
if (dto.isDirectory()) {
links.self(addPath(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, ""), path));
} else {
links.self(addPath(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, ""), path));
}
dto.add(links.build());
}
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
private String addPath(String sourceWithPath, String path) {
return URI.create(sourceWithPath).resolve(path).toASCIIString();
}
private String removeFirstSlash(String source) {
return source.startsWith("/") ? source.substring(1) : source;
}
}

View File

@@ -28,6 +28,8 @@ public class MapperModule extends AbstractModule {
bind(PermissionDtoToPermissionMapper.class).to(Mappers.getMapper(PermissionDtoToPermissionMapper.class).getClass());
bind(PermissionToPermissionDtoMapper.class).to(Mappers.getMapper(PermissionToPermissionDtoMapper.class).getClass());
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
bind(UriInfoStore.class).in(ServletScopes.REQUEST);
}
}

View File

@@ -47,7 +47,7 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
}
}
linksBuilder.single(link("changesets", resourceLinks.changeset().self(target.getNamespace(), target.getName())));
linksBuilder.single(link("sources", resourceLinks.source().self(target.getNamespace(), target.getName())));
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName())));
target.add(linksBuilder.build());
}
}

View File

@@ -290,13 +290,29 @@ class ResourceLinks {
sourceLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, SourceRootResource.class);
}
String self(String namespace, String name) {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("getAll").parameters().href();
String self(String namespace, String name, String revision) {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("getAll").parameters(revision).href();
}
String selfWithoutRevision(String namespace, String name) {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("getAllWithoutRevision").parameters().href();
}
public String source(String namespace, String name, String revision) {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("get").parameters(revision).href();
}
public String sourceWithPath(String namespace, String name, String revision, String path) {
if (revision == null) {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("get").parameters(null, path).href();
} else {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("get").parameters(revision, path).href();
}
}
public String content(String namespace, String name, String revision, String path) {
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("content").parameters().method("get").parameters(revision, path).href();
}
}
public PermissionLinks permission() {
return new PermissionLinks(uriInfoStore.get());

View File

@@ -1,25 +1,75 @@
package sonia.scm.api.v2.resources;
import javax.ws.rs.DefaultValue;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.BrowseCommandBuilder;
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.QueryParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.io.IOException;
public class SourceRootResource {
@GET
@Path("")
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
@DefaultValue("10") @QueryParam("pageSize") int pageSize,
@QueryParam("sortBy") String sortBy,
@DefaultValue("false") @QueryParam("desc") boolean desc) {
throw new UnsupportedOperationException();
private final RepositoryServiceFactory serviceFactory;
private final BrowserResultToBrowserResultDtoMapper browserResultToBrowserResultDtoMapper;
@Inject
public SourceRootResource(RepositoryServiceFactory serviceFactory, BrowserResultToBrowserResultDtoMapper browserResultToBrowserResultDtoMapper) {
this.serviceFactory = serviceFactory;
this.browserResultToBrowserResultDtoMapper = browserResultToBrowserResultDtoMapper;
}
@GET
@Produces(VndMediaType.SOURCE)
@Path("")
public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) {
return getSource(namespace, name, "/", null);
}
@GET
@Produces(VndMediaType.SOURCE)
@Path("{revision}")
public Response get() {
throw new UnsupportedOperationException();
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) {
return getSource(namespace, name, "/", revision);
}
@GET
@Produces(VndMediaType.SOURCE)
@Path("{revision}/{path: .*}")
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
return getSource(namespace, name, path, revision);
}
private Response getSource(String namespace, String repoName, String path, String revision) {
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName);
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand();
browseCommand.setPath(path);
if (revision != null && !revision.isEmpty()) {
browseCommand.setRevision(revision);
}
BrowserResult browserResult = browseCommand.getBrowserResult();
if (browserResult != null) {
return Response.ok(browserResultToBrowserResultDtoMapper.map(browserResult, namespaceAndName)).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
} catch (RepositoryNotFoundException | RevisionNotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
} catch (IOException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
}

View File

@@ -0,0 +1,14 @@
package sonia.scm.api.v2.resources;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class SubRepositoryDto {
private String repositoryUrl;
private String browserUrl;
private String revision;
}