This commit is contained in:
Rene Pfeuffer
2020-02-18 17:56:22 +01:00
parent 1c8088a1c6
commit 6b3f36e7ea
7 changed files with 62 additions and 36 deletions

View File

@@ -49,7 +49,7 @@ import java.util.function.Consumer;
public final class BrowseCommandRequest extends FileBaseCommandRequest public final class BrowseCommandRequest extends FileBaseCommandRequest
{ {
public static final int DEFAULT_REQUEST_LIMIT = 1000; public static final int DEFAULT_REQUEST_LIMIT = 100;
private static final long serialVersionUID = 7956624623516803183L; private static final long serialVersionUID = 7956624623516803183L;
private int proceedFrom; private int proceedFrom;

View File

@@ -9,6 +9,7 @@ import { File, Repository } from "@scm-manager/ui-types";
import { ErrorNotification, Loading, Notification } from "@scm-manager/ui-components"; import { ErrorNotification, Loading, Notification } from "@scm-manager/ui-components";
import { fetchSources, getFetchSourcesFailure, getSources, isFetchSourcesPending } from "../modules/sources"; import { fetchSources, getFetchSourcesFailure, getSources, isFetchSourcesPending } from "../modules/sources";
import FileTreeLeaf from "./FileTreeLeaf"; import FileTreeLeaf from "./FileTreeLeaf";
import queryString from "query-string";
type Props = WithTranslation & { type Props = WithTranslation & {
loading: boolean; loading: boolean;
@@ -18,6 +19,7 @@ type Props = WithTranslation & {
revision: string; revision: string;
path: string; path: string;
baseUrl: string; baseUrl: string;
location: any;
updateSources: () => void; updateSources: () => void;
@@ -85,7 +87,7 @@ class FileTree extends React.Component<Props, State> {
} }
renderSourcesTable() { renderSourcesTable() {
const { tree, revision, path, baseUrl, t } = this.props; const { tree, revision, path, baseUrl, t, location } = this.props;
const files = []; const files = [];
@@ -119,6 +121,7 @@ class FileTree extends React.Component<Props, State> {
} }
if (files && files.length > 0) { if (files && files.length > 0) {
let baseUrlWithRevision = baseUrl; let baseUrlWithRevision = baseUrl;
if (revision) { if (revision) {
baseUrlWithRevision += "/" + encodeURIComponent(revision); baseUrlWithRevision += "/" + encodeURIComponent(revision);
@@ -126,7 +129,13 @@ class FileTree extends React.Component<Props, State> {
baseUrlWithRevision += "/" + encodeURIComponent(tree.revision); baseUrlWithRevision += "/" + encodeURIComponent(tree.revision);
} }
const proceedFrom = queryString.parse(location.search).proceedFrom;
if (proceedFrom) {
baseUrlWithRevision += "?proceedFrom=" + proceedFrom;
}
return ( return (
<>
<table className="table table-hover table-sm is-fullwidth"> <table className="table table-hover table-sm is-fullwidth">
<thead> <thead>
<tr> <tr>
@@ -139,11 +148,13 @@ class FileTree extends React.Component<Props, State> {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{files.map(file => ( {files.map((file: any) => (
<FileTreeLeaf key={file.name} file={file} baseUrl={baseUrlWithRevision} /> <FileTreeLeaf key={file.name} file={file} baseUrl={baseUrlWithRevision} />
))} ))}
</tbody> </tbody>
</table> </table>
{tree.truncated && <h1>TRUNCATED</h1>}
</>
); );
} }
return <Notification type="info">{t("sources.noSources")}</Notification>; return <Notification type="info">{t("sources.noSources")}</Notification>;

View File

@@ -10,6 +10,7 @@ import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.FileObject; import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.SubRepository; import sonia.scm.repository.SubRepository;
import sonia.scm.repository.spi.BrowseCommandRequest;
import javax.inject.Inject; import javax.inject.Inject;
@@ -30,7 +31,7 @@ abstract class BaseFileObjectDtoMapper extends HalAppenderMapper implements Inst
abstract SubRepositoryDto mapSubrepository(SubRepository subRepository); abstract SubRepositoryDto mapSubrepository(SubRepository subRepository);
@ObjectFactory @ObjectFactory
FileObjectDto createDto(@Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult, FileObject fileObject) { FileObjectDto createDto(@Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult, @Context Integer proceedFrom, FileObject fileObject) {
String path = removeFirstSlash(fileObject.getPath()); String path = removeFirstSlash(fileObject.getPath());
Links.Builder links = Links.linkingTo(); Links.Builder links = Links.linkingTo();
if (fileObject.isDirectory()) { if (fileObject.isDirectory()) {
@@ -39,6 +40,9 @@ abstract class BaseFileObjectDtoMapper extends HalAppenderMapper implements Inst
links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision(), path)); links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision(), path));
links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision(), path))); links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision(), path)));
} }
if (fileObject.isTruncated()) {
links.single(link("proceed", resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision(), path) + "?proceedFrom=" + (proceedFrom + BrowseCommandRequest.DEFAULT_REQUEST_LIMIT)));
}
Embedded.Builder embeddedBuilder = embeddedBuilder(); Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(links, embeddedBuilder, namespaceAndName, browserResult, fileObject); applyEnrichers(links, embeddedBuilder, namespaceAndName, browserResult, fileObject);

View File

@@ -1,14 +1,19 @@
package sonia.scm.api.v2.resources; package sonia.scm.api.v2.resources;
import com.google.common.annotations.VisibleForTesting;
import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links; import de.otto.edison.hal.Links;
import org.mapstruct.Context; import org.mapstruct.Context;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.ObjectFactory;
import org.mapstruct.Qualifier; import org.mapstruct.Qualifier;
import sonia.scm.repository.BrowserResult; import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.FileObject; import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.SubRepository;
import sonia.scm.repository.spi.BrowseCommand;
import sonia.scm.repository.spi.BrowseCommandRequest;
import javax.inject.Inject; import javax.inject.Inject;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
@@ -19,19 +24,23 @@ import java.time.Instant;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
@Mapper @Mapper
public abstract class BrowserResultToFileObjectDtoMapper extends BaseFileObjectDtoMapper { public abstract class BrowserResultToFileObjectDtoMapper extends BaseFileObjectDtoMapper {
FileObjectDto map(BrowserResult browserResult, @Context NamespaceAndName namespaceAndName) { FileObjectDto map(BrowserResult browserResult, NamespaceAndName namespaceAndName, int proceedFrom) {
FileObjectDto fileObjectDto = fileObjectToDto(browserResult.getFile(), namespaceAndName, browserResult); FileObjectDto fileObjectDto = fileObjectToDto(browserResult.getFile(), namespaceAndName, browserResult, proceedFrom);
fileObjectDto.setRevision(browserResult.getRevision()); fileObjectDto.setRevision(browserResult.getRevision());
return fileObjectDto; return fileObjectDto;
} }
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
@Mapping(target = "children", qualifiedBy = Children.class) @Mapping(target = "children", qualifiedBy = Children.class)
@Children @Children
protected abstract FileObjectDto fileObjectToDto(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult); protected abstract FileObjectDto fileObjectToDto(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult, @Context Integer proceedFrom);
@Override @Override
void applyEnrichers(Links.Builder links, Embedded.Builder embeddedBuilder, NamespaceAndName namespaceAndName, BrowserResult browserResult, FileObject fileObject) { void applyEnrichers(Links.Builder links, Embedded.Builder embeddedBuilder, NamespaceAndName namespaceAndName, BrowserResult browserResult, FileObject fileObject) {

View File

@@ -15,7 +15,6 @@ import java.util.OptionalLong;
@Getter @Getter
@Setter @Setter
@NoArgsConstructor
public class FileObjectDto extends HalRepresentation { public class FileObjectDto extends HalRepresentation {
private String name; private String name;
private String path; private String path;
@@ -31,6 +30,7 @@ public class FileObjectDto extends HalRepresentation {
private String revision; private String revision;
private boolean partialResult; private boolean partialResult;
private boolean computationAborted; private boolean computationAborted;
private boolean truncated;
public FileObjectDto(Links links, Embedded embedded) { public FileObjectDto(Links links, Embedded embedded) {
super(links, embedded); super(links, embedded);

View File

@@ -8,11 +8,12 @@ import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.Response; import javax.ws.rs.QueryParam;
import java.io.IOException; import java.io.IOException;
import java.net.URLDecoder; import java.net.URLDecoder;
@@ -34,36 +35,37 @@ public class SourceRootResource {
@GET @GET
@Produces(VndMediaType.SOURCE) @Produces(VndMediaType.SOURCE)
@Path("") @Path("")
public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { public FileObjectDto getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name, @DefaultValue("0") @QueryParam("proceedFrom") int proceedFrom) throws IOException {
return getSource(namespace, name, "/", null); return getSource(namespace, name, "/", null, proceedFrom);
} }
@GET @GET
@Produces(VndMediaType.SOURCE) @Produces(VndMediaType.SOURCE)
@Path("{revision}") @Path("{revision}")
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { public FileObjectDto getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @DefaultValue("0") @QueryParam("proceedFrom") int proceedFrom) throws IOException {
return getSource(namespace, name, "/", revision); return getSource(namespace, name, "/", revision, proceedFrom);
} }
@GET @GET
@Produces(VndMediaType.SOURCE) @Produces(VndMediaType.SOURCE)
@Path("{revision}/{path: .*}") @Path("{revision}/{path: .*}")
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) throws IOException { public FileObjectDto get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path, @DefaultValue("0") @QueryParam("proceedFrom") int proceedFrom) throws IOException {
return getSource(namespace, name, path, revision); return getSource(namespace, name, path, revision, proceedFrom);
} }
private Response getSource(String namespace, String repoName, String path, String revision) throws IOException { private FileObjectDto getSource(String namespace, String repoName, String path, String revision, int proceedFrom) throws IOException {
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName); NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName);
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand(); BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand();
browseCommand.setPath(path); browseCommand.setPath(path);
browseCommand.setProceedFrom(proceedFrom);
if (revision != null && !revision.isEmpty()) { if (revision != null && !revision.isEmpty()) {
browseCommand.setRevision(URLDecoder.decode(revision, "UTF-8")); browseCommand.setRevision(URLDecoder.decode(revision, "UTF-8"));
} }
BrowserResult browserResult = browseCommand.getBrowserResult(); BrowserResult browserResult = browseCommand.getBrowserResult();
if (browserResult != null) { if (browserResult != null) {
return Response.ok(browserResultToFileObjectDtoMapper.map(browserResult, namespaceAndName)).build(); return browserResultToFileObjectDtoMapper.map(browserResult, namespaceAndName, proceedFrom);
} else { } else {
throw notFound(entity("Source", path).in("Revision", revision).in(namespaceAndName)); throw notFound(entity("Source", path).in("Revision", revision).in(namespaceAndName));
} }

View File

@@ -66,7 +66,7 @@ public class BrowserResultToFileObjectDtoMapperTest {
public void shouldMapAttributesCorrectly() { public void shouldMapAttributesCorrectly() {
BrowserResult browserResult = createBrowserResult(); BrowserResult browserResult = createBrowserResult();
FileObjectDto dto = mapper.map(browserResult, new NamespaceAndName("foo", "bar")); FileObjectDto dto = mapper.map(browserResult, new NamespaceAndName("foo", "bar"), 0);
assertEqualAttributes(browserResult, dto); assertEqualAttributes(browserResult, dto);
} }
@@ -76,7 +76,7 @@ public class BrowserResultToFileObjectDtoMapperTest {
BrowserResult browserResult = createBrowserResult(); BrowserResult browserResult = createBrowserResult();
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar"); NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
FileObjectDto dto = mapper.map(browserResult, namespaceAndName); FileObjectDto dto = mapper.map(browserResult, namespaceAndName, 0);
assertThat(dto.getEmbedded().getItemsBy("children")).hasSize(2); assertThat(dto.getEmbedded().getItemsBy("children")).hasSize(2);
} }
@@ -86,7 +86,7 @@ public class BrowserResultToFileObjectDtoMapperTest {
BrowserResult browserResult = createBrowserResult(); BrowserResult browserResult = createBrowserResult();
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar"); NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
FileObjectDto dto = mapper.map(browserResult, namespaceAndName); FileObjectDto dto = mapper.map(browserResult, namespaceAndName, 0);
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).contains("path"); assertThat(dto.getLinks().getLinkBy("self").get().getHref()).contains("path");
} }