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));