mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 15:35:49 +01:00
#8771 Merged Permission endpoints
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package sonia.scm.it;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static sonia.scm.it.RestUtil.given;
|
||||
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RepositoryAccessITCase {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
private final String repositoryType;
|
||||
private RepositoryUtil repositoryUtil;
|
||||
|
||||
public RepositoryAccessITCase(String repositoryType) {
|
||||
this.repositoryType = repositoryType;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<String> createParameters() {
|
||||
return availableScmTypes();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initClient() throws IOException {
|
||||
TestData.createDefault();
|
||||
repositoryUtil = new RepositoryUtil(repositoryType, tempFolder.getRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindBranches() throws IOException {
|
||||
assumeFalse("There are no branches for SVN", repositoryType.equals("svn"));
|
||||
|
||||
repositoryUtil.createAndCommitFile("a.txt", "a");
|
||||
|
||||
String branchesUrl = given()
|
||||
.when()
|
||||
.get(TestData.getDefaultRepositoryUrl(repositoryType))
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.path("_links.branches.href");
|
||||
|
||||
Object branchName = given()
|
||||
.when()
|
||||
.get(branchesUrl)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.path("_embedded.branches[0].name");
|
||||
|
||||
assertNotNull(branchName);
|
||||
}
|
||||
}
|
||||
62
scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java
Normal file
62
scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package sonia.scm.it;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import org.apache.http.HttpStatus;
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.repository.client.api.ClientCommand;
|
||||
import sonia.scm.repository.client.api.RepositoryClient;
|
||||
import sonia.scm.repository.client.api.RepositoryClientFactory;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static sonia.scm.it.RestUtil.ADMIN_PASSWORD;
|
||||
import static sonia.scm.it.RestUtil.ADMIN_USERNAME;
|
||||
import static sonia.scm.it.RestUtil.given;
|
||||
|
||||
public class RepositoryUtil {
|
||||
|
||||
private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory();
|
||||
|
||||
private final RepositoryClient repositoryClient;
|
||||
private final File folder;
|
||||
|
||||
RepositoryUtil(String repositoryType, File folder) throws IOException {
|
||||
this.repositoryClient = createRepositoryClient(repositoryType, folder);
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
static RepositoryClient createRepositoryClient(String repositoryType, File folder) throws IOException {
|
||||
String httpProtocolUrl = given(VndMediaType.REPOSITORY)
|
||||
|
||||
.when()
|
||||
.get(TestData.getDefaultRepositoryUrl(repositoryType))
|
||||
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.path("_links.httpProtocol.href");
|
||||
|
||||
|
||||
return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, ADMIN_USERNAME, ADMIN_PASSWORD, folder);
|
||||
}
|
||||
|
||||
void createAndCommitFile(String fileName, String content) throws IOException {
|
||||
Files.write(content, new File(folder, fileName), Charsets.UTF_8);
|
||||
repositoryClient.getAddCommand().add(fileName);
|
||||
commit("added " + fileName);
|
||||
}
|
||||
|
||||
Changeset commit(String message) throws IOException {
|
||||
Changeset changeset = repositoryClient.getCommitCommand().commit(
|
||||
new Person("scmadmin", "scmadmin@scm-manager.org"), message
|
||||
);
|
||||
if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) {
|
||||
repositoryClient.getPushCommand().push();
|
||||
}
|
||||
return changeset;
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,18 @@ public class RestUtil {
|
||||
return REST_BASE_URL.resolve(path);
|
||||
}
|
||||
|
||||
public static final String ADMIN_USERNAME = "scmadmin";
|
||||
public static final String ADMIN_PASSWORD = "scmadmin";
|
||||
|
||||
public static RequestSpecification given(String mediaType) {
|
||||
return RestAssured.given()
|
||||
.contentType(mediaType)
|
||||
.accept(mediaType)
|
||||
.auth().preemptive().basic("scmadmin", "scmadmin");
|
||||
.auth().preemptive().basic(ADMIN_USERNAME, ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
public static RequestSpecification given() {
|
||||
return RestAssured.given()
|
||||
.auth().preemptive().basic(ADMIN_USERNAME, ADMIN_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
treeWalk.setRecursive(Util.nonNull(path).contains("/"));
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("load content for {} at {}", path, revId.name());
|
||||
}
|
||||
|
||||
revWalk = new RevWalk(repo);
|
||||
|
||||
RevCommit entry = revWalk.parseCommit(revId);
|
||||
RevTree revTree = entry.getTree();
|
||||
|
||||
if (revTree != null)
|
||||
{
|
||||
treeWalk.addTree(revTree);
|
||||
}
|
||||
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)
|
||||
{
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
ObjectLoader loader = repo.open(blobId);
|
||||
|
||||
loader.copyTo(output);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Not a blob, its something else (tree, gitlink)
|
||||
throw new PathNotFoundException(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PathNotFoundException(path);
|
||||
}
|
||||
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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
|
||||
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("/"));
|
||||
|
||||
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());
|
||||
}
|
||||
RevTree revTree = entry.getTree();
|
||||
|
||||
if (revTree != null) {
|
||||
treeWalk.addTree(revTree);
|
||||
} else {
|
||||
logger.error("could not find tree for {}", revId.name());
|
||||
}
|
||||
|
||||
treeWalk.setFilter(PathFilter.create(path));
|
||||
|
||||
if (treeWalk.next() && treeWalk.getFileMode(0).getObjectType() == Constants.OBJ_BLOB) {
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
ObjectLoader loader = repo.open(blobId);
|
||||
|
||||
return new ClosableObjectLoaderContainer(loader, treeWalk, revWalk);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
@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;
|
||||
new HgCatCommand(cmdContext, repository).getCatResult(request, baos);
|
||||
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();
|
||||
|
||||
|
||||
@@ -226,7 +226,18 @@
|
||||
<artifactId>compiler</artifactId>
|
||||
<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,9 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
@Slf4j
|
||||
public class PermissionRootResourceTest {
|
||||
public static final String REPOSITORY_NAMESPACE = "repo_namespace";
|
||||
public static final String REPOSITORY_NAME = "repo";
|
||||
private static final String REPOSITORY_NAMESPACE = "repo_namespace";
|
||||
private static final String REPOSITORY_NAME = "repo";
|
||||
private static final String PERMISSION_NAME = "perm";
|
||||
|
||||
|
||||
private static final String PATH_OF_ALL_PERMISSIONS = REPOSITORY_NAMESPACE + "/" + REPOSITORY_NAME + "/permissions/";
|
||||
private static final String PATH_OF_ONE_PERMISSION = PATH_OF_ALL_PERMISSIONS + PERMISSION_NAME;
|
||||
private static final String PERMISSION_TEST_PAYLOAD = "{ \"name\" : \"permission_name\", \"type\" : \"READ\" }";
|
||||
@@ -93,7 +91,6 @@ public class PermissionRootResourceTest {
|
||||
.content(PERMISSION_TEST_PAYLOAD)
|
||||
.path(PATH_OF_ONE_PERMISSION);
|
||||
|
||||
|
||||
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
|
||||
@Rule
|
||||
@@ -102,7 +99,6 @@ public class PermissionRootResourceTest {
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
|
||||
private final URI baseUri = URI.create("/");
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@@ -112,17 +108,15 @@ public class PermissionRootResourceTest {
|
||||
@InjectMocks
|
||||
private PermissionDtoToPermissionMapperImpl permissionDtoToPermissionMapper;
|
||||
|
||||
|
||||
private PermissionRootResource permissionRootResource;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
initMocks(this);
|
||||
permissionRootResource = spy(new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, resourceLinks, repositoryManager));
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||
.of(new RepositoryResource(null, null, null, null, null, null, null, MockProvider.of(permissionRootResource))), null);
|
||||
.of(new RepositoryResource(null, null, null, null, null, null, null,null, MockProvider.of(permissionRootResource))), null);
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
dispatcher.getProviderFactory().registerProvider(RepositoryNotFoundExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(PermissionNotFoundExceptionMapper.class);
|
||||
@@ -130,32 +124,50 @@ public class PermissionRootResourceTest {
|
||||
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("test endpoints on missing repository and user is Admin")
|
||||
Stream<DynamicTest> missedRepositoryTestFactory() {
|
||||
return createDynamicTestsToAssertResponses(
|
||||
requestGETAllPermissions.expectedResponseStatus(404),
|
||||
requestGETPermission.expectedResponseStatus(404),
|
||||
requestPOSTPermission.expectedResponseStatus(404),
|
||||
requestDELETEPermission.expectedResponseStatus(404),
|
||||
requestPUTPermission.expectedResponseStatus(404));
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("test endpoints on missing permission and user is Admin")
|
||||
Stream<DynamicTest> missedPermissionTestFactory() {
|
||||
authorizedUserHasARepository();
|
||||
return createDynamicTestsToAssertResponses(
|
||||
requestGETPermission.expectedResponseStatus(404),
|
||||
requestPOSTPermission.expectedResponseStatus(201),
|
||||
requestGETAllPermissions.expectedResponseStatus(200),
|
||||
requestDELETEPermission.expectedResponseStatus(204),
|
||||
requestPUTPermission.expectedResponseStatus(404));
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("test endpoints on missing permission and user is not Admin")
|
||||
Stream<DynamicTest> missedPermissionUserForbiddenTestFactory() {
|
||||
Repository mockRepository = mock(Repository.class);
|
||||
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
|
||||
doThrow(AuthorizationException.class).when(permissionRootResource).checkUserPermitted(mockRepository);
|
||||
when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository);
|
||||
return createDynamicTestsToAssertResponses(
|
||||
requestGETPermission.expectedResponseStatus(401),
|
||||
requestPOSTPermission.expectedResponseStatus(401),
|
||||
requestGETAllPermissions.expectedResponseStatus(401),
|
||||
requestDELETEPermission.expectedResponseStatus(401),
|
||||
requestPUTPermission.expectedResponseStatus(401));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetAllPermissions() {
|
||||
authorizedUserHasARepositoryWithPermissions(TEST_PERMISSIONS);
|
||||
assertExpectedRequest(requestGETAllPermissions
|
||||
.expectedResponseStatus(200)
|
||||
.responseValidator((response) -> {
|
||||
String body = response.getContentAsString();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
List<PermissionDto> actualPermissionDtos = mapper.readValue(body, new TypeReference<List<PermissionDto>>() {
|
||||
});
|
||||
assertThat(actualPermissionDtos)
|
||||
.as("response payload match permission object models")
|
||||
.hasSize(TEST_PERMISSIONS.size())
|
||||
.usingRecursiveFieldByFieldElementComparator()
|
||||
.containsExactlyInAnyOrder(getExpectedPermissionDtos(TEST_PERMISSIONS))
|
||||
;
|
||||
} catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
})
|
||||
);
|
||||
assertGettingExpectedPermissions(ImmutableList.copyOf(TEST_PERMISSIONS));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGetPermissionByName() {
|
||||
authorizedUserHasARepositoryWithPermissions(TEST_PERMISSIONS);
|
||||
@@ -179,7 +191,6 @@ public class PermissionRootResourceTest {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGetCreatedPermissions() {
|
||||
authorizedUserHasARepositoryWithPermissions(TEST_PERMISSIONS);
|
||||
@@ -207,7 +218,6 @@ public class PermissionRootResourceTest {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGetUpdatedPermissions() {
|
||||
authorizedUserHasARepositoryWithPermissions(TEST_PERMISSIONS);
|
||||
@@ -287,7 +297,6 @@ public class PermissionRootResourceTest {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private PermissionDto[] getExpectedPermissionDtos(ArrayList<Permission> permissions) {
|
||||
return permissions
|
||||
.stream()
|
||||
@@ -309,30 +318,6 @@ public class PermissionRootResourceTest {
|
||||
return result;
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("test endpoints on missing repository and user is Admin")
|
||||
Stream<DynamicTest> missedRepositoryTestFactory() {
|
||||
return createDynamicTestsToAssertResponses(
|
||||
requestGETAllPermissions.expectedResponseStatus(404),
|
||||
requestGETPermission.expectedResponseStatus(404),
|
||||
requestPOSTPermission.expectedResponseStatus(404),
|
||||
requestDELETEPermission.expectedResponseStatus(404),
|
||||
requestPUTPermission.expectedResponseStatus(404));
|
||||
}
|
||||
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("test endpoints on missing permission and user is Admin")
|
||||
Stream<DynamicTest> missedPermissionTestFactory() {
|
||||
authorizedUserHasARepository();
|
||||
return createDynamicTestsToAssertResponses(
|
||||
requestGETPermission.expectedResponseStatus(404),
|
||||
requestPOSTPermission.expectedResponseStatus(201),
|
||||
requestGETAllPermissions.expectedResponseStatus(200),
|
||||
requestDELETEPermission.expectedResponseStatus(204),
|
||||
requestPUTPermission.expectedResponseStatus(404));
|
||||
}
|
||||
|
||||
private Repository authorizedUserHasARepository() {
|
||||
Repository mockRepository = mock(Repository.class);
|
||||
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
|
||||
@@ -347,25 +332,7 @@ public class PermissionRootResourceTest {
|
||||
when(authorizedUserHasARepository().getPermissions()).thenReturn(permissions);
|
||||
}
|
||||
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("test endpoints on missing permission and user is not Admin")
|
||||
Stream<DynamicTest> missedPermissionUserForbiddenTestFactory() {
|
||||
Repository mockRepository = mock(Repository.class);
|
||||
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
|
||||
doThrow(AuthorizationException.class).when(permissionRootResource).checkUserPermitted(mockRepository);
|
||||
when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository);
|
||||
return createDynamicTestsToAssertResponses(
|
||||
requestGETPermission.expectedResponseStatus(401),
|
||||
requestPOSTPermission.expectedResponseStatus(401),
|
||||
requestGETAllPermissions.expectedResponseStatus(401),
|
||||
requestDELETEPermission.expectedResponseStatus(401),
|
||||
requestPUTPermission.expectedResponseStatus(401));
|
||||
}
|
||||
|
||||
|
||||
private Stream<DynamicTest> createDynamicTestsToAssertResponses(ExpectedRequest... expectedRequests) {
|
||||
|
||||
return Stream.of(expectedRequests)
|
||||
.map(entry -> dynamicTest("the endpoint " + entry.description + " should return the status code " + entry.expectedResponseStatus, () -> assertExpectedRequest(entry)));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
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