diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml
index d0003c1f7c..965bddf2b0 100644
--- a/scm-webapp/pom.xml
+++ b/scm-webapp/pom.xml
@@ -226,7 +226,18 @@
compiler
${mustache.version}
-
+
+
+ com.github.sdorra
+ spotter-core
+ 1.1.0
+
+
+
+ org.apache.tika
+ tika-core
+ 1.18
+
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java
new file mode 100644
index 0000000000..dc14d00e54
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java
@@ -0,0 +1,96 @@
+package sonia.scm.api.v2.resources;
+
+import com.github.sdorra.spotter.ContentType;
+import com.github.sdorra.spotter.ContentTypes;
+import com.github.sdorra.spotter.Language;
+import sonia.scm.repository.NamespaceAndName;
+import sonia.scm.repository.PathNotFoundException;
+import sonia.scm.repository.RepositoryException;
+import sonia.scm.repository.RepositoryNotFoundException;
+import sonia.scm.repository.api.RepositoryService;
+import sonia.scm.repository.api.RepositoryServiceFactory;
+
+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.core.Response;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Optional;
+
+public class ContentResource {
+
+ private final RepositoryServiceFactory servicefactory;
+
+ @Inject
+ public ContentResource(RepositoryServiceFactory servicefactory) {
+ this.servicefactory = servicefactory;
+ }
+
+ @GET
+ @Path("{revision}/{path: .*}")
+ 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))) {
+ try {
+ byte[] content = getContent(revision, path, repositoryService);
+ Response.ResponseBuilder responseBuilder = Response.ok(content);
+ appendContentType(path, content, responseBuilder);
+ return responseBuilder.build();
+ } catch (PathNotFoundException e) {
+ return Response.status(404).build();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return Response.status(500).entity(e.getMessage()).build();
+ } catch (RepositoryException e) {
+ e.printStackTrace();
+ return Response.status(500).entity(e.getMessage()).build();
+ }
+ } catch (RepositoryNotFoundException e) {
+ return Response.status(404).build();
+ }
+ }
+
+ @HEAD
+ @Path("{revision}/{path: .*}")
+ 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))) {
+ try {
+ byte[] content = getContent(revision, path, repositoryService);
+
+ Response.ResponseBuilder responseBuilder = Response.ok();
+
+ appendContentType(path, content, responseBuilder);
+ return responseBuilder.build();
+ } catch (PathNotFoundException e) {
+ return Response.status(404).build();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return Response.status(500).entity(e.getMessage()).build();
+ } catch (RepositoryException e) {
+ e.printStackTrace();
+ return Response.status(500).entity(e.getMessage()).build();
+ }
+ } catch (RepositoryNotFoundException e) {
+ return Response.status(404).build();
+ }
+ }
+
+ private void appendContentType(String path, byte[] content, Response.ResponseBuilder responseBuilder) {
+ ContentType contentType = ContentTypes.detect(path, content);
+ System.out.println("Content-Type: " + contentType);
+
+ Optional language = contentType.getLanguage();
+ if (language.isPresent()) {
+ responseBuilder.header("Content-Type", contentType);
+ }
+ responseBuilder.header("Content-Length", content.length);
+ }
+
+ private byte[] getContent(@PathParam("revision") String revision, @PathParam("path") String path, RepositoryService repositoryService) throws IOException, RepositoryException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ repositoryService.getCatCommand().setRevision(revision).retriveContent(outputStream, path);
+ return outputStream.toByteArray();
+ }
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java
index 43aa6de608..817eb29f11 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java
@@ -35,6 +35,7 @@ public class RepositoryResource {
private final Provider branchRootResource;
private final Provider changesetRootResource;
private final Provider sourceRootResource;
+ private final Provider contentResource;
private final Provider permissionRootResource;
@Inject
@@ -44,7 +45,7 @@ public class RepositoryResource {
Provider tagRootResource,
Provider branchRootResource,
Provider changesetRootResource,
- Provider sourceRootResource, Provider permissionRootResource) {
+ Provider sourceRootResource, Provider contentResource, Provider 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();
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java
index 711a5cf196..1b23b18f27 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java
@@ -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);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java
index 75d09a2414..d47b26e5eb 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java
@@ -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));