mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 09:25:43 +01:00
Merge 2.0.0-m3
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -305,7 +305,7 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -33,24 +33,19 @@
|
||||
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.spi.CatCommand;
|
||||
import sonia.scm.repository.spi.CatCommandRequest;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
@@ -106,23 +101,31 @@ public final class CatCommandBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the content of the given file to the outputstream.
|
||||
* Passes the content of the given file to the output stream.
|
||||
*
|
||||
* @param outputStream outputstream for the content
|
||||
* @param outputStream output stream for the content
|
||||
* @param path file path
|
||||
*
|
||||
* @return {@code this}
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public CatCommandBuilder retriveContent(OutputStream outputStream,
|
||||
String path)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
public void retriveContent(OutputStream outputStream, String path) throws IOException, RepositoryException {
|
||||
getCatResult(outputStream, path);
|
||||
}
|
||||
|
||||
return this;
|
||||
/**
|
||||
* Returns an output stream with the file content.
|
||||
*
|
||||
* @param path file path
|
||||
*/
|
||||
public InputStream getStream(String path) throws IOException, RepositoryException {
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(path),
|
||||
"path is required");
|
||||
|
||||
CatCommandRequest requestClone = request.clone();
|
||||
|
||||
requestClone.setPath(path);
|
||||
|
||||
logger.debug("create cat stream for {}", requestClone);
|
||||
|
||||
return catCommand.getCatResultStream(requestClone);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@@ -33,13 +33,10 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
@@ -47,19 +44,9 @@ import java.io.OutputStream;
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.17
|
||||
*/
|
||||
public interface CatCommand
|
||||
{
|
||||
public interface CatCommand {
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param output
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public void getCatResult(CatCommandRequest request, OutputStream output)
|
||||
throws IOException, RepositoryException;
|
||||
void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RepositoryException;
|
||||
|
||||
InputStream getCatResultStream(CatCommandRequest request) throws IOException, RepositoryException;
|
||||
}
|
||||
|
||||
@@ -32,158 +32,133 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitCatCommand extends AbstractGitCommand implements CatCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* the logger for GitCatCommand
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GitCatCommand.class);
|
||||
public class GitCatCommand extends AbstractGitCommand implements CatCommand {
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
private static final Logger logger = LoggerFactory.getLogger(GitCatCommand.class);
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
public GitCatCommand(GitContext context,
|
||||
sonia.scm.repository.Repository repository)
|
||||
{
|
||||
public GitCatCommand(GitContext context, sonia.scm.repository.Repository repository) {
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param output
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public void getCatResult(CatCommandRequest request, OutputStream output)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RepositoryException {
|
||||
logger.debug("try to read content for {}", request);
|
||||
|
||||
org.eclipse.jgit.lib.Repository repo = open();
|
||||
|
||||
ObjectId revId = getCommitOrDefault(repo, request.getRevision());
|
||||
getContent(repo, revId, request.getPath(), output);
|
||||
try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(request)) {
|
||||
closableObjectLoaderContainer.objectLoader.copyTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param revId
|
||||
* @param path
|
||||
* @param output
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId,
|
||||
String path, OutputStream output)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
TreeWalk treeWalk = null;
|
||||
RevWalk revWalk = null;
|
||||
@Override
|
||||
public InputStream getCatResultStream(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
logger.debug("try to read content for {}", request);
|
||||
return new InputStreamWrapper(getLoader(request));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
treeWalk = new TreeWalk(repo);
|
||||
void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path, OutputStream output) throws IOException, RepositoryException {
|
||||
try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(repo, revId, path)) {
|
||||
closableObjectLoaderContainer.objectLoader.copyTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
private ClosableObjectLoaderContainer getLoader(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
org.eclipse.jgit.lib.Repository repo = open();
|
||||
ObjectId revId = getCommitOrDefault(repo, request.getRevision());
|
||||
return getLoader(repo, revId, request.getPath());
|
||||
}
|
||||
|
||||
private ClosableObjectLoaderContainer getLoader(Repository repo, ObjectId revId, String path) throws IOException, RepositoryException {
|
||||
TreeWalk treeWalk = new TreeWalk(repo);
|
||||
treeWalk.setRecursive(Util.nonNull(path).contains("/"));
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("load content for {} at {}", path, revId.name());
|
||||
|
||||
RevWalk revWalk = new RevWalk(repo);
|
||||
|
||||
RevCommit entry = null;
|
||||
try {
|
||||
entry = revWalk.parseCommit(revId);
|
||||
} catch (MissingObjectException e) {
|
||||
throw new RevisionNotFoundException(revId.getName());
|
||||
}
|
||||
|
||||
revWalk = new RevWalk(repo);
|
||||
|
||||
RevCommit entry = revWalk.parseCommit(revId);
|
||||
RevTree revTree = entry.getTree();
|
||||
|
||||
if (revTree != null)
|
||||
{
|
||||
if (revTree != null) {
|
||||
treeWalk.addTree(revTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
logger.error("could not find tree for {}", revId.name());
|
||||
}
|
||||
|
||||
treeWalk.setFilter(PathFilter.create(path));
|
||||
|
||||
if (treeWalk.next())
|
||||
{
|
||||
|
||||
// Path exists
|
||||
if (treeWalk.getFileMode(0).getObjectType() == Constants.OBJ_BLOB)
|
||||
{
|
||||
if (treeWalk.next() && treeWalk.getFileMode(0).getObjectType() == Constants.OBJ_BLOB) {
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
ObjectLoader loader = repo.open(blobId);
|
||||
|
||||
loader.copyTo(output);
|
||||
return new ClosableObjectLoaderContainer(loader, treeWalk, revWalk);
|
||||
} else {
|
||||
throw new PathNotFoundException(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Not a blob, its something else (tree, gitlink)
|
||||
throw new PathNotFoundException(path);
|
||||
private static class ClosableObjectLoaderContainer implements Closeable {
|
||||
private final ObjectLoader objectLoader;
|
||||
private final TreeWalk treeWalk;
|
||||
private final RevWalk revWalk;
|
||||
|
||||
private ClosableObjectLoaderContainer(ObjectLoader objectLoader, TreeWalk treeWalk, RevWalk revWalk) {
|
||||
this.objectLoader = objectLoader;
|
||||
this.treeWalk = treeWalk;
|
||||
this.revWalk = revWalk;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PathNotFoundException(path);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.release(treeWalk);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InputStreamWrapper extends FilterInputStream {
|
||||
|
||||
private final ClosableObjectLoaderContainer container;
|
||||
|
||||
private InputStreamWrapper(ClosableObjectLoaderContainer container) throws IOException {
|
||||
super(container.objectLoader.openStream());
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
} finally {
|
||||
container.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +32,17 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import sonia.scm.repository.GitConstants;
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import sonia.scm.repository.GitConstants;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GitCatCommand}.
|
||||
@@ -53,15 +51,8 @@ import sonia.scm.repository.GitConstants;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitCatCommandTest extends AbstractGitCommandTestBase
|
||||
{
|
||||
public class GitCatCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
/**
|
||||
* Tests cat command with default branch.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultBranch() throws IOException, RepositoryException {
|
||||
// without default branch, the repository head should be used
|
||||
@@ -75,16 +66,8 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase
|
||||
assertEquals("a and b", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testCat() throws IOException, RepositoryException
|
||||
{
|
||||
public void testCat() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("a.txt");
|
||||
@@ -92,36 +75,46 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase
|
||||
assertEquals("a and b", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testSimpleCat() throws IOException, RepositoryException
|
||||
{
|
||||
public void testSimpleCat() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("b.txt");
|
||||
assertEquals("b", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private String execute(CatCommandRequest request)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
@Test(expected = PathNotFoundException.class)
|
||||
public void testUnknownFile() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("unknown");
|
||||
execute(request);
|
||||
}
|
||||
|
||||
@Test(expected = RevisionNotFoundException.class)
|
||||
public void testUnknownRevision() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setRevision("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
request.setPath("a.txt");
|
||||
execute(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStream() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
request.setPath("b.txt");
|
||||
|
||||
InputStream catResultStream = new GitCatCommand(createContext(), repository).getCatResultStream(request);
|
||||
|
||||
assertEquals('b', catResultStream.read());
|
||||
assertEquals('\n', catResultStream.read());
|
||||
assertEquals(-1, catResultStream.read());
|
||||
|
||||
catResultStream.close();
|
||||
}
|
||||
|
||||
private String execute(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
String content = null;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
|
||||
@@ -33,71 +33,44 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.aragost.javahg.commands.ExecutionException;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Closeables;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.web.HgUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgCatCommand extends AbstractCommand implements CatCommand
|
||||
{
|
||||
public class HgCatCommand extends AbstractCommand implements CatCommand {
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
HgCatCommand(HgCommandContext context, Repository repository)
|
||||
{
|
||||
HgCatCommand(HgCommandContext context, Repository repository) {
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param output
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public void getCatResult(CatCommandRequest request, OutputStream output)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RepositoryException {
|
||||
InputStream input = getCatResultStream(request);
|
||||
try {
|
||||
ByteStreams.copy(input, output);
|
||||
} finally {
|
||||
Closeables.close(input, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getCatResultStream(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
com.aragost.javahg.commands.CatCommand cmd =
|
||||
com.aragost.javahg.commands.CatCommand.on(open());
|
||||
|
||||
cmd.rev(HgUtil.getRevision(request.getRevision()));
|
||||
|
||||
InputStream input = null;
|
||||
|
||||
try
|
||||
{
|
||||
input = cmd.execute(request.getPath());
|
||||
ByteStreams.copy(input, output);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Closeables.close(input, true);
|
||||
try {
|
||||
return cmd.execute(request.getPath());
|
||||
} catch (ExecutionException e) {
|
||||
throw new RepositoryException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,36 +33,19 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgCatCommandTest extends AbstractHgCommandTestBase
|
||||
{
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class HgCatCommandTest extends AbstractHgCommandTestBase {
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testCat() throws IOException, RepositoryException
|
||||
{
|
||||
public void testCat() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("a.txt");
|
||||
@@ -70,48 +53,48 @@ public class HgCatCommandTest extends AbstractHgCommandTestBase
|
||||
assertEquals("a", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testSimpleCat() throws IOException, RepositoryException
|
||||
{
|
||||
public void testSimpleCat() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("b.txt");
|
||||
assertEquals("b", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private String execute(CatCommandRequest request)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
String content = null;
|
||||
@Test(expected = RepositoryException.class)
|
||||
public void testUnknownFile() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("unknown");
|
||||
execute(request);
|
||||
}
|
||||
|
||||
@Test(expected = RepositoryException.class)
|
||||
public void testUnknownRevision() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setRevision("abc");
|
||||
request.setPath("a.txt");
|
||||
execute(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStream() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
request.setPath("b.txt");
|
||||
|
||||
InputStream catResultStream = new HgCatCommand(cmdContext, repository).getCatResultStream(request);
|
||||
|
||||
assertEquals('b', catResultStream.read());
|
||||
assertEquals('\n', catResultStream.read());
|
||||
assertEquals(-1, catResultStream.read());
|
||||
|
||||
catResultStream.close();
|
||||
}
|
||||
|
||||
private String execute(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
new HgCatCommand(cmdContext, repository).getCatResult(request, baos);
|
||||
}
|
||||
finally
|
||||
{
|
||||
content = baos.toString().trim();
|
||||
}
|
||||
|
||||
return content;
|
||||
return baos.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,22 +37,26 @@ package sonia.scm.repository.spi;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNErrorCode;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.SVNProperties;
|
||||
import org.tmatesoft.svn.core.io.SVNRepository;
|
||||
import org.tmatesoft.svn.core.wc.SVNClientManager;
|
||||
import org.tmatesoft.svn.core.wc.admin.SVNLookClient;
|
||||
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.repository.SvnUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -120,6 +124,16 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getCatResultStream(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
// There seems to be no method creating an input stream as a result, so
|
||||
// we have no other possibility then to copy the content into a buffer and
|
||||
// stream it from there.
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
getCatResult(request, output);
|
||||
return new ByteArrayInputStream(output.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -146,6 +160,17 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand
|
||||
}
|
||||
catch (SVNException ex)
|
||||
{
|
||||
handleSvnException(request, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSvnException(CatCommandRequest request, SVNException ex) throws RepositoryException {
|
||||
int svnErrorCode = ex.getErrorMessage().getErrorCode().getCode();
|
||||
if (SVNErrorCode.FS_NOT_FOUND.getCode() == svnErrorCode) {
|
||||
throw new PathNotFoundException(request.getPath());
|
||||
} else if (SVNErrorCode.FS_NO_SUCH_REVISION.getCode() == svnErrorCode) {
|
||||
throw new RevisionNotFoundException(request.getRevision());
|
||||
} else {
|
||||
throw new RepositoryException("could not get content from revision", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,36 +32,23 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class SvnCatCommandTest extends AbstractSvnCommandTestBase
|
||||
{
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
public class SvnCatCommandTest extends AbstractSvnCommandTestBase {
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testCat() throws IOException, RepositoryException
|
||||
{
|
||||
public void testCat() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("a.txt");
|
||||
@@ -69,36 +56,50 @@ public class SvnCatCommandTest extends AbstractSvnCommandTestBase
|
||||
assertEquals("a", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testSimpleCat() throws IOException, RepositoryException
|
||||
{
|
||||
public void testSimpleCat() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("c/d.txt");
|
||||
assertEquals("d", execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private String execute(CatCommandRequest request)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
@Test(expected = PathNotFoundException.class)
|
||||
public void testUnknownFile() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("unknown");
|
||||
request.setRevision("1");
|
||||
|
||||
execute(request);
|
||||
}
|
||||
|
||||
@Test(expected = RevisionNotFoundException.class)
|
||||
public void testUnknownRevision() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
|
||||
request.setPath("a.txt");
|
||||
request.setRevision("42");
|
||||
|
||||
execute(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStream() throws IOException, RepositoryException {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
request.setPath("a.txt");
|
||||
request.setRevision("1");
|
||||
|
||||
InputStream catResultStream = new SvnCatCommand(createContext(), repository).getCatResultStream(request);
|
||||
|
||||
assertEquals('a', catResultStream.read());
|
||||
assertEquals('\n', catResultStream.read());
|
||||
assertEquals(-1, catResultStream.read());
|
||||
|
||||
catResultStream.close();
|
||||
}
|
||||
|
||||
private String execute(CatCommandRequest request) throws IOException, RepositoryException {
|
||||
String content = null;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
|
||||
@@ -227,6 +227,17 @@
|
||||
<version>${mustache.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.sdorra</groupId>
|
||||
<artifactId>spotter-core</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tika</groupId>
|
||||
<artifactId>tika-core</artifactId>
|
||||
<version>1.18</version>
|
||||
</dependency>
|
||||
<!-- test scope -->
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -35,26 +35,20 @@ package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.Closeables;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.repository.api.CatCommandBuilder;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.spotter.ContentType;
|
||||
import com.github.sdorra.spotter.ContentTypes;
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
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.util.IOUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HEAD;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ContentResource {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ContentResource.class);
|
||||
private static final int HEAD_BUFFER_SIZE = 1024;
|
||||
|
||||
private final RepositoryServiceFactory serviceFactory;
|
||||
|
||||
@Inject
|
||||
public ContentResource(RepositoryServiceFactory serviceFactory) {
|
||||
this.serviceFactory = serviceFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of a file for the given revision in the repository. The content type depends on the file
|
||||
* content and can be discovered calling <code>HEAD</code> on the same URL. If a programming languge could be
|
||||
* recognized, this will be given in the header <code>Language</code>.
|
||||
*
|
||||
* @param namespace the namespace of the repository
|
||||
* @param name the name of the repository
|
||||
* @param revision the revision
|
||||
* @param path The path of the file
|
||||
*
|
||||
*/
|
||||
@GET
|
||||
@Path("{revision}/{path: .*}")
|
||||
@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 repository"),
|
||||
@ResponseCode(code = 404, condition = "not found, no repository with the specified name available in the namespace"),
|
||||
@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) {
|
||||
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) {
|
||||
LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e);
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
|
||||
private StreamingOutput createStreamingOutput(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path, RepositoryService repositoryService) {
|
||||
return os -> {
|
||||
try {
|
||||
repositoryService.getCatCommand().setRevision(revision).retriveContent(os, path);
|
||||
os.close();
|
||||
} catch (PathNotFoundException e) {
|
||||
LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e);
|
||||
throw new WebApplicationException(Status.NOT_FOUND);
|
||||
} catch (RepositoryException e) {
|
||||
LOG.info("error reading repository resource {} from {}/{}", path, namespace, name, e);
|
||||
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content type and the programming language (if it can be detected) of a file for the given revision in
|
||||
* the repository. The programming language will be given in the header <code>Language</code>.
|
||||
*
|
||||
* @param namespace the namespace of the repository
|
||||
* @param name the name of the repository
|
||||
* @param revision the revision
|
||||
* @param path The path of the file
|
||||
*
|
||||
*/
|
||||
@HEAD
|
||||
@Path("{revision}/{path: .*}")
|
||||
@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 repository"),
|
||||
@ResponseCode(code = 404, condition = "not found, no repository with the specified name available in the namespace"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response metadata(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
|
||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||
Response.ResponseBuilder responseBuilder = Response.ok();
|
||||
return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder);
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e);
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
|
||||
private Response createContentHeader(String namespace, String name, String revision, String path, RepositoryService repositoryService, Response.ResponseBuilder responseBuilder) {
|
||||
try {
|
||||
appendContentHeader(path, getHead(revision, path, repositoryService), responseBuilder);
|
||||
} catch (PathNotFoundException e) {
|
||||
LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e);
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
} catch (RevisionNotFoundException e) {
|
||||
LOG.debug("revision '{}' not found in repository {}/{}", revision, namespace, name, e);
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
} catch (IOException | RepositoryException e) {
|
||||
LOG.info("error reading repository resource {} from {}/{}", path, namespace, name, e);
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
|
||||
}
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
private void appendContentHeader(String path, byte[] head, Response.ResponseBuilder responseBuilder) {
|
||||
ContentType contentType = ContentTypes.detect(path, head);
|
||||
responseBuilder.header("Content-Type", contentType.getRaw());
|
||||
contentType.getLanguage().ifPresent(language -> responseBuilder.header("Language", language));
|
||||
}
|
||||
|
||||
private byte[] getHead(String revision, String path, RepositoryService repositoryService) throws IOException, RepositoryException {
|
||||
InputStream stream = repositoryService.getCatCommand().setRevision(revision).getStream(path);
|
||||
try {
|
||||
byte[] buffer = new byte[HEAD_BUFFER_SIZE];
|
||||
int length = stream.read(buffer);
|
||||
if (length < buffer.length) {
|
||||
return Arrays.copyOf(buffer, length);
|
||||
} else {
|
||||
return buffer;
|
||||
}
|
||||
} finally {
|
||||
IOUtil.close(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public class RepositoryResource {
|
||||
private final Provider<BranchRootResource> branchRootResource;
|
||||
private final Provider<ChangesetRootResource> changesetRootResource;
|
||||
private final Provider<SourceRootResource> sourceRootResource;
|
||||
private final Provider<ContentResource> contentResource;
|
||||
private final Provider<PermissionRootResource> permissionRootResource;
|
||||
|
||||
@Inject
|
||||
@@ -44,7 +45,7 @@ public class RepositoryResource {
|
||||
Provider<TagRootResource> tagRootResource,
|
||||
Provider<BranchRootResource> branchRootResource,
|
||||
Provider<ChangesetRootResource> changesetRootResource,
|
||||
Provider<SourceRootResource> sourceRootResource, Provider<PermissionRootResource> permissionRootResource) {
|
||||
Provider<SourceRootResource> sourceRootResource, Provider<ContentResource> contentResource, Provider<PermissionRootResource> permissionRootResource) {
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.manager = manager;
|
||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||
@@ -53,6 +54,7 @@ public class RepositoryResource {
|
||||
this.branchRootResource = branchRootResource;
|
||||
this.changesetRootResource = changesetRootResource;
|
||||
this.sourceRootResource = sourceRootResource;
|
||||
this.contentResource = contentResource;
|
||||
this.permissionRootResource = permissionRootResource;
|
||||
}
|
||||
|
||||
@@ -151,6 +153,11 @@ public class RepositoryResource {
|
||||
return sourceRootResource.get();
|
||||
}
|
||||
|
||||
@Path("content/")
|
||||
public ContentResource content() {
|
||||
return contentResource.get();
|
||||
}
|
||||
|
||||
@Path("permissions/")
|
||||
public PermissionRootResource permissions() {
|
||||
return permissionRootResource.get();
|
||||
|
||||
@@ -45,7 +45,7 @@ public class BranchRootResourceTest {
|
||||
public void prepareEnvironment() throws Exception {
|
||||
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
|
||||
BranchRootResource branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null)), null);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null)), null);
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
|
||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.PathNotFoundException;
|
||||
import sonia.scm.repository.RepositoryNotFoundException;
|
||||
import sonia.scm.repository.api.CatCommandBuilder;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.AdditionalMatchers.not;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ContentResourceTest {
|
||||
|
||||
private static final String NAMESPACE = "space";
|
||||
private static final String REPO_NAME = "name";
|
||||
private static final String REV = "rev";
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private RepositoryServiceFactory repositoryServiceFactory;
|
||||
|
||||
@InjectMocks
|
||||
private ContentResource contentResource;
|
||||
|
||||
private CatCommandBuilder catCommand;
|
||||
|
||||
@Before
|
||||
public void initService() throws Exception {
|
||||
NamespaceAndName existingNamespaceAndName = new NamespaceAndName(NAMESPACE, REPO_NAME);
|
||||
RepositoryService repositoryService = repositoryServiceFactory.create(existingNamespaceAndName);
|
||||
catCommand = repositoryService.getCatCommand();
|
||||
when(catCommand.setRevision(REV)).thenReturn(catCommand);
|
||||
|
||||
// defaults for unknown things
|
||||
doThrow(new RepositoryNotFoundException("x")).when(repositoryServiceFactory).create(not(eq(existingNamespaceAndName)));
|
||||
doThrow(new PathNotFoundException("x")).when(catCommand).getStream(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadSimpleFile() throws Exception {
|
||||
mockContent("file", "Hello".getBytes());
|
||||
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "file");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
ByteArrayOutputStream baos = readOutputStream(response);
|
||||
|
||||
assertEquals("Hello", baos.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleMissingFile() {
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "doesNotExist");
|
||||
assertEquals(404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleMissingRepository() {
|
||||
Response response = contentResource.get("no", "repo", REV, "anything");
|
||||
assertEquals(404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRecognizeTikaSourceCode() throws Exception {
|
||||
mockContentFromResource("SomeGoCode.go");
|
||||
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "SomeGoCode.go");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertEquals("GO", response.getHeaderString("Language"));
|
||||
assertEquals("text/x-go", response.getHeaderString("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRecognizeSpecialSourceCode() throws Exception {
|
||||
mockContentFromResource("Dockerfile");
|
||||
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "Dockerfile");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertEquals("DOCKERFILE", response.getHeaderString("Language"));
|
||||
assertEquals("text/plain", response.getHeaderString("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRecognizeShebangSourceCode() throws Exception {
|
||||
mockContentFromResource("someScript.sh");
|
||||
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "someScript.sh");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertEquals("PYTHON", response.getHeaderString("Language"));
|
||||
assertEquals("application/x-sh", response.getHeaderString("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleRandomByteFile() throws Exception {
|
||||
mockContentFromResource("JustBytes");
|
||||
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "JustBytes");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertFalse(response.getHeaders().containsKey("Language"));
|
||||
assertEquals("application/octet-stream", response.getHeaderString("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotReadCompleteFileForHead() throws Exception {
|
||||
FailingAfterSomeBytesStream stream = new FailingAfterSomeBytesStream();
|
||||
doAnswer(invocation -> stream).when(catCommand).getStream(eq("readHeadOnly"));
|
||||
|
||||
Response response = contentResource.metadata(NAMESPACE, REPO_NAME, REV, "readHeadOnly");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertEquals("application/octet-stream", response.getHeaderString("Content-Type"));
|
||||
assertTrue("stream has to be closed after reading head", stream.isClosed());
|
||||
}
|
||||
|
||||
private void mockContentFromResource(String fileName) throws Exception {
|
||||
URL url = Resources.getResource(fileName);
|
||||
mockContent(fileName, Resources.toByteArray(url));
|
||||
}
|
||||
|
||||
private void mockContent(String path, byte[] content) throws Exception {
|
||||
doAnswer(invocation -> {
|
||||
OutputStream outputStream = (OutputStream) invocation.getArguments()[0];
|
||||
outputStream.write(content);
|
||||
outputStream.close();
|
||||
return null;
|
||||
}).when(catCommand).retriveContent(any(), eq(path));
|
||||
doAnswer(invocation -> new ByteArrayInputStream(content)).when(catCommand).getStream(eq(path));
|
||||
}
|
||||
|
||||
private ByteArrayOutputStream readOutputStream(Response response) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
((StreamingOutput) response.getEntity()).write(baos);
|
||||
return baos;
|
||||
}
|
||||
|
||||
private static class FailingAfterSomeBytesStream extends InputStream {
|
||||
private int bytesRead = 0;
|
||||
private boolean closed = false;
|
||||
@Override
|
||||
public int read() {
|
||||
if (++bytesRead > 1024) {
|
||||
fail("read too many bytes");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class RepositoryRootResourceTest {
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
initMocks(this);
|
||||
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null);
|
||||
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null);
|
||||
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
|
||||
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));
|
||||
|
||||
@@ -67,6 +67,7 @@ public class SourceRootResourceTest {
|
||||
null,
|
||||
null,
|
||||
MockProvider.of(sourceRootResource),
|
||||
null,
|
||||
null)),
|
||||
null);
|
||||
|
||||
|
||||
8
scm-webapp/src/test/resources/Dockerfile
Normal file
8
scm-webapp/src/test/resources/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM ubuntu
|
||||
|
||||
COPY nothing /nowhere
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
EXEC shutdhown -h now
|
||||
|
||||
BIN
scm-webapp/src/test/resources/JustBytes
Normal file
BIN
scm-webapp/src/test/resources/JustBytes
Normal file
Binary file not shown.
1
scm-webapp/src/test/resources/SomeGoCode.go
Normal file
1
scm-webapp/src/test/resources/SomeGoCode.go
Normal file
@@ -0,0 +1 @@
|
||||
package resources
|
||||
6
scm-webapp/src/test/resources/someScript.sh
Normal file
6
scm-webapp/src/test/resources/someScript.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
for f in * ; do
|
||||
ls $f
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user