From 60526ba38ead50eab8646222c83aaa0534dc4a8d Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Fri, 10 Aug 2018 15:39:14 +0200 Subject: [PATCH 01/21] Started implementing the sources feature --- .../api/v2/resources/BrowserResultDto.java | 18 +++++ ...BrowserResultToBrowserResultDtoMapper.java | 9 +++ .../scm/api/v2/resources/FileObjectDto.java | 28 ++++++++ .../FileObjectToFileObjectDtoMapper.java | 9 +++ .../api/v2/resources/SourceRootResource.java | 43 +++++++++--- .../FileObjectToFileObjectDtoMapperTest.java | 69 +++++++++++++++++++ 6 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java new file mode 100644 index 0000000000..b62f5fa124 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java @@ -0,0 +1,18 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Collection; + +@Getter +@Setter +@NoArgsConstructor +public class BrowserResultDto extends HalRepresentation { + private String revision; + private String tag; + private String branch; + private Collection files; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java new file mode 100644 index 0000000000..ecc59900e5 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java @@ -0,0 +1,9 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.Mapper; +import sonia.scm.repository.BrowserResult; + +@Mapper +public abstract class BrowserResultToBrowserResultDtoMapper extends BaseMapper { + +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java new file mode 100644 index 0000000000..2f4c7a07c7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java @@ -0,0 +1,28 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Links; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.Instant; + +@Getter +@Setter +@NoArgsConstructor +public class FileObjectDto extends HalRepresentation { + private String name; + private String path; + private boolean directory; + private String description; + private int length; + // TODO: What about subrepos? + private Instant lastModified; + + @Override + @SuppressWarnings("squid:S1185") // We want to have this method available in this package + protected HalRepresentation add(Links links) { + return super.add(links); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java new file mode 100644 index 0000000000..86a5e5454b --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -0,0 +1,9 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.Mapper; +import sonia.scm.repository.FileObject; + +@Mapper +public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper { + +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index d05cc7ea6e..46d8f02c91 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -1,20 +1,45 @@ package sonia.scm.api.v2.resources; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; +import sonia.scm.repository.BrowserResult; +import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.RepositoryException; +import sonia.scm.repository.RepositoryNotFoundException; +import sonia.scm.repository.api.BrowseCommandBuilder; +import sonia.scm.repository.api.RepositoryService; +import sonia.scm.repository.api.RepositoryServiceFactory; + +import javax.inject.Inject; +import javax.ws.rs.*; import javax.ws.rs.core.Response; +import java.io.IOException; public class SourceRootResource { + private final RepositoryServiceFactory serviceFactory; + + @Inject + public SourceRootResource(RepositoryServiceFactory serviceFactory) { + this.serviceFactory = serviceFactory; + } + @GET @Path("") - public Response getAll(@DefaultValue("0") @QueryParam("page") int page, - @DefaultValue("10") @QueryParam("pageSize") int pageSize, - @QueryParam("sortBy") String sortBy, - @DefaultValue("false") @QueryParam("desc") boolean desc) { - throw new UnsupportedOperationException(); + public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) { + + BrowserResult browserResult = null; + try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { + BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand(); + browseCommand.setPath("/"); + browserResult = browseCommand.getBrowserResult(); + } catch (RepositoryNotFoundException e) { + e.printStackTrace(); + } catch (RepositoryException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return Response.ok(browserResult.toString()).build(); } @GET diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java new file mode 100644 index 0000000000..7babd4b329 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java @@ -0,0 +1,69 @@ +package sonia.scm.api.v2.resources; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectThreadState; +import org.apache.shiro.util.ThreadContext; +import org.apache.shiro.util.ThreadState; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.repository.FileObject; + +import java.net.URI; +import java.net.URISyntaxException; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; + +@RunWith(MockitoJUnitRunner.class) +public class FileObjectToFileObjectDtoMapperTest { + private final URI baseUri = URI.create("http://example.com/base/"); + + @InjectMocks + private FileObjectToFileObjectDtoMapperImpl mapper; + + private final Subject subject = mock(Subject.class); + private final ThreadState subjectThreadState = new SubjectThreadState(subject); + + + @Before + public void init() throws URISyntaxException { + initMocks(this); + subjectThreadState.bind(); + ThreadContext.bind(subject); + } + + + @Test + public void shouldMapAttributesCorrectly() { + FileObject fileObject = createFileObject(); + FileObjectDto dto = mapper.map(fileObject); + + assertEqualAttributes(fileObject, dto); + } + + + private FileObject createFileObject() { + FileObject fileObject = new FileObject(); + fileObject.setName("foo"); + fileObject.setDescription("bar"); + fileObject.setPath("/foo/bar"); + fileObject.setDirectory(false); + fileObject.setLength(100); + fileObject.setLastModified(123L); + return fileObject; + } + + //TODO: subrepo + private void assertEqualAttributes(FileObject fileObject, FileObjectDto dto) { + assertEquals(fileObject.getName(), dto.getName()); + assertEquals(fileObject.getDescription(), dto.getDescription()); + assertEquals(fileObject.getPath(), dto.getPath()); + assertEquals(fileObject.isDirectory(), dto.isDirectory()); + assertEquals(fileObject.getLength(), dto.getLength()); + assertEquals((long)fileObject.getLastModified(), dto.getLastModified().toEpochMilli()); + } +} From f375e076e452ca5e3231ed80f42d00fe9a28eb76 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Mon, 13 Aug 2018 17:31:45 +0200 Subject: [PATCH 02/21] Implemented BrowserResultMapper --- .../main/java/sonia/scm/web/VndMediaType.java | 1 + .../api/v2/resources/BrowserResultDto.java | 19 +++- .../api/v2/resources/BrowserResultMapper.java | 31 ++++++ ...BrowserResultToBrowserResultDtoMapper.java | 9 -- ...ctDtoMapper.java => FileObjectMapper.java} | 3 +- .../scm/api/v2/resources/MapperModule.java | 2 + .../api/v2/resources/SourceRootResource.java | 16 ++- .../v2/resources/BrowserResultMapperTest.java | 100 ++++++++++++++++++ ...perTest.java => FileObjectMapperTest.java} | 12 +-- 9 files changed, 167 insertions(+), 26 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java rename scm-webapp/src/main/java/sonia/scm/api/v2/resources/{FileObjectToFileObjectDtoMapper.java => FileObjectMapper.java} (53%) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java rename scm-webapp/src/test/java/sonia/scm/api/v2/resources/{FileObjectToFileObjectDtoMapperTest.java => FileObjectMapperTest.java} (85%) diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index 1e439a6a16..9dd6b68d36 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -25,6 +25,7 @@ public class VndMediaType { public static final String REPOSITORY_TYPE_COLLECTION = PREFIX + "repositoryTypeCollection" + SUFFIX; public static final String REPOSITORY_TYPE = PREFIX + "repositoryType" + SUFFIX; public static final String ME = PREFIX + "me" + SUFFIX; + public static final String SOURCE = PREFIX + "source" + SUFFIX; private VndMediaType() { } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java index b62f5fa124..fe0f98930c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java @@ -5,14 +5,27 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.util.Collection; +import java.util.Iterator; +import java.util.List; @Getter @Setter @NoArgsConstructor -public class BrowserResultDto extends HalRepresentation { +public class BrowserResultDto extends HalRepresentation implements Iterable { private String revision; private String tag; private String branch; - private Collection files; + private List files; + + @Override + public Iterator iterator() { + Iterator it = null; + + if (files != null) + { + it = files.iterator(); + } + + return it; + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java new file mode 100644 index 0000000000..1690225a72 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java @@ -0,0 +1,31 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.Mapper; +import sonia.scm.repository.BrowserResult; +import sonia.scm.repository.FileObject; + +import java.util.ArrayList; +import java.util.List; + +@Mapper +public abstract class BrowserResultMapper extends BaseMapper { + + abstract FileObjectDto mapFileObject(FileObject fileObject); + + public BrowserResultDto map(BrowserResult browserResult) { + BrowserResultDto browserResultDto = new BrowserResultDto(); + + browserResultDto.setTag(browserResult.getTag()); + browserResultDto.setBranch(browserResult.getBranch()); + browserResultDto.setRevision(browserResult.getRevision()); + + List fileObjectDtoList = new ArrayList<>(); + for (FileObject fileObject : browserResult.getFiles()) { + fileObjectDtoList.add(mapFileObject(fileObject)); + } + + browserResultDto.setFiles(fileObjectDtoList); + + return browserResultDto; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java deleted file mode 100644 index ecc59900e5..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package sonia.scm.api.v2.resources; - -import org.mapstruct.Mapper; -import sonia.scm.repository.BrowserResult; - -@Mapper -public abstract class BrowserResultToBrowserResultDtoMapper extends BaseMapper { - -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java similarity index 53% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index 86a5e5454b..87abc22380 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -4,6 +4,5 @@ import org.mapstruct.Mapper; import sonia.scm.repository.FileObject; @Mapper -public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper { - +public abstract class FileObjectMapper extends BaseMapper { } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index 0ac6929689..5247a17ac8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -26,6 +26,8 @@ public class MapperModule extends AbstractModule { bind(BranchToBranchDtoMapper.class).to(Mappers.getMapper(BranchToBranchDtoMapper.class).getClass()); + bind(BrowserResultMapper.class).to(Mappers.getMapper(BrowserResultMapper.class).getClass()); + bind(UriInfoStore.class).in(ServletScopes.REQUEST); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index 46d8f02c91..58586768e5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -7,22 +7,31 @@ import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.BrowseCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.ws.rs.*; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import java.io.IOException; public class SourceRootResource { private final RepositoryServiceFactory serviceFactory; + private final BrowserResultMapper browserResultMapper; + + @Inject - public SourceRootResource(RepositoryServiceFactory serviceFactory) { + public SourceRootResource(RepositoryServiceFactory serviceFactory, BrowserResultMapper browserResultMapper) { this.serviceFactory = serviceFactory; + this.browserResultMapper = browserResultMapper; } @GET + @Produces(VndMediaType.SOURCE) @Path("") public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) { @@ -38,8 +47,7 @@ public class SourceRootResource { } catch (IOException e) { e.printStackTrace(); } - - return Response.ok(browserResult.toString()).build(); + return Response.ok(browserResultMapper.map(browserResult)).build(); } @GET diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java new file mode 100644 index 0000000000..c05f93ad7a --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java @@ -0,0 +1,100 @@ +package sonia.scm.api.v2.resources; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectThreadState; +import org.apache.shiro.util.ThreadContext; +import org.apache.shiro.util.ThreadState; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.repository.BrowserResult; +import sonia.scm.repository.FileObject; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; + +@RunWith(MockitoJUnitRunner.class) +public class BrowserResultMapperTest { + + @InjectMocks + private BrowserResultMapperImpl mapper; + + private final Subject subject = mock(Subject.class); + private final ThreadState subjectThreadState = new SubjectThreadState(subject); + + + @Before + public void init() { + initMocks(this); + subjectThreadState.bind(); + ThreadContext.bind(subject); + } + + @Test + public void shouldMapAttributesCorrectly() { + BrowserResult browserResult = createBrowserResult(); + + BrowserResultDto dto = mapper.map(browserResult); + + assertEqualAttributes(browserResult, dto); + + assertEqualFileObjectAttributes(browserResult.getFiles().get(0), dto.getFiles().get(0)); + assertEqualFileObjectAttributes(browserResult.getFiles().get(1), dto.getFiles().get(1)); + } + + private BrowserResult createBrowserResult() { + BrowserResult browserResult = new BrowserResult(); + browserResult.setTag("Tag"); + browserResult.setRevision("Revision"); + browserResult.setBranch("Branch"); + browserResult.setFiles(createFileObjects()); + + return browserResult; + } + + private List createFileObjects() { + List fileObjects = new ArrayList<>(); + + FileObject fileObject1 = new FileObject(); + fileObject1.setName("FO 1"); + fileObject1.setLength(100); + fileObject1.setLastModified(0L); + fileObject1.setPath("/path/object/1"); + fileObject1.setDescription("description of file object 1"); + fileObject1.setDirectory(false); + + FileObject fileObject2 = new FileObject(); + fileObject2.setName("FO 2"); + fileObject2.setLength(100); + fileObject2.setLastModified(101L); + fileObject2.setPath("/path/object/2"); + fileObject2.setDescription("description of file object 2"); + fileObject2.setDirectory(true); + + fileObjects.add(fileObject1); + fileObjects.add(fileObject2); + return fileObjects; + } + + private void assertEqualAttributes(BrowserResult browserResult, BrowserResultDto dto) { + assertThat(dto.getTag()).isEqualTo(browserResult.getTag()); + assertThat(dto.getBranch()).isEqualTo(browserResult.getBranch()); + assertThat(dto.getRevision()).isEqualTo(browserResult.getRevision()); + } + + private void assertEqualFileObjectAttributes(FileObject fileObject, FileObjectDto dto) { + assertThat(dto.getName()).isEqualTo(fileObject.getName()); + assertThat(dto.getLength()).isEqualTo(fileObject.getLength()); + assertThat(dto.getLastModified()).isEqualTo(Instant.ofEpochMilli(fileObject.getLastModified())); + assertThat(dto.isDirectory()).isEqualTo(fileObject.isDirectory()); + assertThat(dto.getDescription()).isEqualTo(fileObject.getDescription()); + assertThat(dto.getPath()).isEqualTo(fileObject.getPath()); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectMapperTest.java similarity index 85% rename from scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java rename to scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectMapperTest.java index 7babd4b329..b06dad6fb2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectMapperTest.java @@ -11,26 +11,22 @@ import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.FileObject; -import java.net.URI; -import java.net.URISyntaxException; - -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.MockitoAnnotations.initMocks; @RunWith(MockitoJUnitRunner.class) -public class FileObjectToFileObjectDtoMapperTest { - private final URI baseUri = URI.create("http://example.com/base/"); +public class FileObjectMapperTest { @InjectMocks - private FileObjectToFileObjectDtoMapperImpl mapper; + private FileObjectMapperImpl mapper; private final Subject subject = mock(Subject.class); private final ThreadState subjectThreadState = new SubjectThreadState(subject); @Before - public void init() throws URISyntaxException { + public void init() { initMocks(this); subjectThreadState.bind(); ThreadContext.bind(subject); From f3925fa3116c50044b649244928d430445e214a6 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Tue, 14 Aug 2018 17:16:10 +0200 Subject: [PATCH 03/21] Added links to sources ressource / refactored code and fixed issues --- .../api/v2/resources/BrowserResultDto.java | 7 ++ .../api/v2/resources/BrowserResultMapper.java | 29 ++++++-- .../api/v2/resources/FileObjectMapper.java | 17 +++++ .../scm/api/v2/resources/MapperModule.java | 2 +- .../RepositoryToRepositoryDtoMapper.java | 2 +- .../scm/api/v2/resources/ResourceLinks.java | 10 ++- .../api/v2/resources/SourceRootResource.java | 26 ++++--- .../v2/resources/BrowserResultMapperTest.java | 72 ++++++++++--------- .../api/v2/resources/ResourceLinksTest.java | 9 ++- 9 files changed, 120 insertions(+), 54 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java index fe0f98930c..12c227b65c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Links; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -17,6 +18,12 @@ public class BrowserResultDto extends HalRepresentation implements Iterable files; + @Override + @SuppressWarnings("squid:S1185") // We want to have this method available in this package + protected HalRepresentation add(Links links) { + return super.add(links); + } + @Override public Iterator iterator() { Iterator it = null; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java index 1690225a72..6ab054bd74 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java @@ -1,18 +1,27 @@ package sonia.scm.api.v2.resources; -import org.mapstruct.Mapper; +import de.otto.edison.hal.Links; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; +import sonia.scm.repository.NamespaceAndName; +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; -@Mapper -public abstract class BrowserResultMapper extends BaseMapper { +public class BrowserResultMapper { - abstract FileObjectDto mapFileObject(FileObject fileObject); + @Inject + private FileObjectMapper fileObjectMapper; - public BrowserResultDto map(BrowserResult browserResult) { + @Inject + private ResourceLinks resourceLinks; + + private FileObjectDto mapFileObject(FileObject fileObject, NamespaceAndName namespaceAndName, String revision) { + return fileObjectMapper.map(fileObject, namespaceAndName, revision); + } + + public BrowserResultDto map(BrowserResult browserResult, NamespaceAndName namespaceAndName) { BrowserResultDto browserResultDto = new BrowserResultDto(); browserResultDto.setTag(browserResult.getTag()); @@ -21,11 +30,17 @@ public abstract class BrowserResultMapper extends BaseMapper fileObjectDtoList = new ArrayList<>(); for (FileObject fileObject : browserResult.getFiles()) { - fileObjectDtoList.add(mapFileObject(fileObject)); + fileObjectDtoList.add(mapFileObject(fileObject, namespaceAndName, browserResult.getRevision())); } browserResultDto.setFiles(fileObjectDtoList); - + this.addLinks(browserResult, browserResultDto, namespaceAndName); return browserResultDto; } + + private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName) { + dto.add(Links.linkingTo().self(resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision())).build()); + } + + } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index 87abc22380..013f7e1125 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -1,8 +1,25 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Links; +import org.mapstruct.AfterMapping; +import org.mapstruct.Context; import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; import sonia.scm.repository.FileObject; +import sonia.scm.repository.NamespaceAndName; + +import javax.inject.Inject; @Mapper public abstract class FileObjectMapper extends BaseMapper { + + @Inject + private ResourceLinks resourceLinks; + + protected abstract FileObjectDto map(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context String revision); + + @AfterMapping + void addLinks(FileObject fileObject, @MappingTarget FileObjectDto dto, @Context NamespaceAndName namespaceAndName, @Context String revision) { + dto.add(Links.linkingTo().self(resourceLinks.source().withPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, fileObject.getName())).build()); + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index 5247a17ac8..5ecdaee23d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -26,7 +26,7 @@ public class MapperModule extends AbstractModule { bind(BranchToBranchDtoMapper.class).to(Mappers.getMapper(BranchToBranchDtoMapper.class).getClass()); - bind(BrowserResultMapper.class).to(Mappers.getMapper(BrowserResultMapper.class).getClass()); + bind(FileObjectMapper.class).to(Mappers.getMapper(FileObjectMapper.class).getClass()); bind(UriInfoStore.class).in(ServletScopes.REQUEST); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java index ca30a09286..d20e2f66dc 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -36,7 +36,7 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper createFileObjects() { List fileObjects = new ArrayList<>(); - FileObject fileObject1 = new FileObject(); - fileObject1.setName("FO 1"); - fileObject1.setLength(100); - fileObject1.setLastModified(0L); - fileObject1.setPath("/path/object/1"); - fileObject1.setDescription("description of file object 1"); - fileObject1.setDirectory(false); - - FileObject fileObject2 = new FileObject(); - fileObject2.setName("FO 2"); - fileObject2.setLength(100); - fileObject2.setLastModified(101L); - fileObject2.setPath("/path/object/2"); - fileObject2.setDescription("description of file object 2"); - fileObject2.setDirectory(true); - fileObjects.add(fileObject1); fileObjects.add(fileObject2); return fileObjects; @@ -89,12 +105,4 @@ public class BrowserResultMapperTest { assertThat(dto.getRevision()).isEqualTo(browserResult.getRevision()); } - private void assertEqualFileObjectAttributes(FileObject fileObject, FileObjectDto dto) { - assertThat(dto.getName()).isEqualTo(fileObject.getName()); - assertThat(dto.getLength()).isEqualTo(fileObject.getLength()); - assertThat(dto.getLastModified()).isEqualTo(Instant.ofEpochMilli(fileObject.getLastModified())); - assertThat(dto.isDirectory()).isEqualTo(fileObject.isDirectory()); - assertThat(dto.getDescription()).isEqualTo(fileObject.getDescription()); - assertThat(dto.getPath()).isEqualTo(fileObject.getPath()); - } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java index 000e628f26..430fea9748 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksTest.java @@ -147,13 +147,18 @@ public class ResourceLinksTest { @Test public void shouldCreateCorrectSourceCollectionUrl() { - String url = resourceLinks.source().self("space", "repo"); + String url = resourceLinks.source().withoutRevision("space", "repo"); assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources/", url); } + @Test + public void shouldCreateCorrectSourceUrlWithFilename() { + String url = resourceLinks.source().withPath("foo", "bar", "rev", "file"); + assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "foo/bar/sources/rev/file", url); + } @Test public void shouldCreateCorrectPermissionCollectionUrl() { - String url = resourceLinks.source().self("space", "repo"); + String url = resourceLinks.source().withoutRevision("space", "repo"); assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources/", url); } From eceea012b005edfe6590e6a0c672848a688172b6 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Wed, 15 Aug 2018 10:26:32 +0200 Subject: [PATCH 04/21] Added optional revision path param to SourceRootResource#getAll --- .../scm/api/v2/resources/BranchToBranchDtoMapper.java | 8 ++------ .../sonia/scm/api/v2/resources/FileObjectMapper.java | 2 +- .../v2/resources/RepositoryToRepositoryDtoMapper.java | 2 +- .../java/sonia/scm/api/v2/resources/ResourceLinks.java | 8 ++++---- .../sonia/scm/api/v2/resources/SourceRootResource.java | 7 +++++-- .../sonia/scm/api/v2/resources/ResourceLinksTest.java | 10 +++++----- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index 8d616ffb77..9cb8b21248 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -1,11 +1,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; -import org.mapstruct.Context; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.*; import sonia.scm.repository.Branch; import sonia.scm.repository.NamespaceAndName; @@ -29,7 +25,7 @@ public abstract class BranchToBranchDtoMapper { .self(resourceLinks.branch().self(namespaceAndName, target.getName())) .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, target.getName())).build()) .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()) - .single(linkBuilder("source", resourceLinks.source().source(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()); + .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()); target.add(linksBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index 013f7e1125..28326255f3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -20,6 +20,6 @@ public abstract class FileObjectMapper extends BaseMapper Date: Wed, 15 Aug 2018 13:40:41 +0200 Subject: [PATCH 05/21] Implemented resource method to get single file --- .../api/v2/resources/SourceRootResource.java | 20 ++- .../v2/resources/SourceRootResourceTest.java | 155 ++++++++++++++++++ 2 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index 5b9c7a916e..9f2709cb43 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -33,13 +33,23 @@ public class SourceRootResource { @Produces(VndMediaType.SOURCE) @Path("{revision : (\\w+)?}") public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) { + return getSource(namespace, name, "/", revision); + } + @GET + @Produces(VndMediaType.SOURCE) + @Path("{revision}/{path: .*}") + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) { + return getSource(namespace, name, path, revision); + } + + private Response getSource(String namespace, String repoName, String path, String revision) { BrowserResult browserResult; Response response; - NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); + NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand(); - browseCommand.setPath("/"); + browseCommand.setPath(path); if (revision != null && !revision.isEmpty()) { browseCommand.setRevision(revision); } @@ -58,10 +68,4 @@ public class SourceRootResource { } return response; } - - @GET - @Path("{revision}/{path: .*}") - public Response get() { - throw new UnsupportedOperationException(); - } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java new file mode 100644 index 0000000000..d4e2ea03b9 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java @@ -0,0 +1,155 @@ +package sonia.scm.api.v2.resources; + +import org.jboss.resteasy.core.Dispatcher; +import org.jboss.resteasy.mock.MockDispatcherFactory; +import org.jboss.resteasy.mock.MockHttpRequest; +import org.jboss.resteasy.mock.MockHttpResponse; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.repository.*; +import sonia.scm.repository.api.BrowseCommandBuilder; +import sonia.scm.repository.api.RepositoryService; +import sonia.scm.repository.api.RepositoryServiceFactory; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + + +@RunWith(MockitoJUnitRunner.Silent.class) +public class SourceRootResourceTest { + + private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private final URI baseUri = URI.create("/"); + private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); + + @Mock + private RepositoryServiceFactory serviceFactory; + @Mock + private RepositoryService service; + @Mock + private BrowseCommandBuilder browseCommandBuilder; + + @Mock + private FileObjectMapper fileObjectMapper; + + @InjectMocks + private BrowserResultMapper browserResultMapper; + + + @Before + public void prepareEnvironment() throws Exception { + when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service); + when(service.getBrowseCommand()).thenReturn(browseCommandBuilder); + + FileObjectDto dto = new FileObjectDto(); + dto.setName("name"); + dto.setLength(1024); + + when(fileObjectMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString())).thenReturn(dto); + SourceRootResource sourceRootResource = new SourceRootResource(serviceFactory, browserResultMapper); + RepositoryRootResource repositoryRootResource = + new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, + null, + null, + null, + null, + null, + MockProvider.of(sourceRootResource), + null)), + null); + + dispatcher.getRegistry().addSingletonResource(repositoryRootResource); + } + + @Test + public void shouldReturnSources() throws URISyntaxException, IOException, RepositoryException { + BrowserResult result = createBrowserResult(); + when(browseCommandBuilder.getBrowserResult()).thenReturn(result); + MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources"); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getContentAsString()).contains("\"revision\":\"revision\""); + assertThat(response.getContentAsString()).contains("\"tag\":\"tag\""); + assertThat(response.getContentAsString()).contains("\"branch\":\"branch\""); + assertThat(response.getContentAsString()).contains("\"files\":"); + } + + @Test + public void shouldReturn404IfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException { + when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class); + MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources"); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + assertThat(response.getStatus()).isEqualTo(404); + } + + @Test + public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, RepositoryException { + BrowserResult browserResult = new BrowserResult(); + browserResult.setBranch("abc"); + browserResult.setRevision("revision"); + browserResult.setTag("tag"); + FileObject fileObject = new FileObject(); + fileObject.setName("File Object!"); + + browserResult.setFiles(Arrays.asList(fileObject)); + + when(browseCommandBuilder.getBrowserResult()).thenReturn(browserResult); + MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources/revision/fileabc"); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getContentAsString()).contains("\"revision\":\"revision\""); + } + + @Test + public void shouldGet404ForSingleFileIfRepoNotFound() throws URISyntaxException, RepositoryException { + when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class); + + MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources/revision/fileabc"); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + assertThat(response.getStatus()).isEqualTo(404); + } + + private BrowserResult createBrowserResult() { + return new BrowserResult("revision", "tag", "branch", createFileObjects()); + } + + private List createFileObjects() { + FileObject fileObject1 = new FileObject(); + fileObject1.setName("FO 1"); + fileObject1.setDirectory(false); + fileObject1.setDescription("File object 1"); + fileObject1.setPath("/foo/bar/fo1"); + fileObject1.setLength(1024L); + fileObject1.setLastModified(0L); + + FileObject fileObject2 = new FileObject(); + fileObject2.setName("FO 2"); + fileObject2.setDirectory(true); + fileObject2.setDescription("File object 2"); + fileObject2.setPath("/foo/bar/fo2"); + fileObject2.setLength(4096L); + fileObject2.setLastModified(1234L); + + return Arrays.asList(fileObject1, fileObject2); + } +} From edac761d67fbf83a036fe7d1c770e2963e5db526 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Thu, 16 Aug 2018 15:20:20 +0200 Subject: [PATCH 06/21] Added content link to FileObjectDto --- .../scm/api/v2/resources/FileObjectDto.java | 2 +- .../api/v2/resources/FileObjectMapper.java | 10 +++- .../scm/api/v2/resources/ResourceLinks.java | 4 ++ .../api/v2/resources/SubRepositoryDto.java | 14 ++++++ .../v2/resources/FileObjectMapperTest.java | 49 ++++++++++++++----- 5 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/SubRepositoryDto.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java index 2f4c7a07c7..ab4986554a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java @@ -17,8 +17,8 @@ public class FileObjectDto extends HalRepresentation { private boolean directory; private String description; private int length; - // TODO: What about subrepos? private Instant lastModified; + private SubRepositoryDto subRepository; @Override @SuppressWarnings("squid:S1185") // We want to have this method available in this package diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index 28326255f3..4363a8a9b1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -7,9 +7,12 @@ import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; import sonia.scm.repository.FileObject; import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.SubRepository; import javax.inject.Inject; +import static de.otto.edison.hal.Link.link; + @Mapper public abstract class FileObjectMapper extends BaseMapper { @@ -18,8 +21,13 @@ public abstract class FileObjectMapper extends BaseMapper Date: Thu, 16 Aug 2018 17:32:38 +0200 Subject: [PATCH 07/21] Fixed wildcard import --- .../sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index 9cb8b21248..7ab3ef25a8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -1,7 +1,11 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; -import org.mapstruct.*; +import org.mapstruct.AfterMapping; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; import sonia.scm.repository.Branch; import sonia.scm.repository.NamespaceAndName; From 6a7987481abe7449a5e5ae1c8fe6ff01bee90d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 17 Aug 2018 09:33:45 +0200 Subject: [PATCH 08/21] Peer review --- .../sonia/scm/it/RepositoryAccessITCase.java | 32 +++++++++++++++++++ .../java/sonia/scm/repository/SvnUtil.java | 12 +++---- .../api/v2/resources/BrowserResultDto.java | 2 ++ .../api/v2/resources/BrowserResultMapper.java | 6 +++- .../api/v2/resources/FileObjectMapper.java | 11 ++++--- .../scm/api/v2/resources/ResourceLinks.java | 10 ++++-- .../api/v2/resources/SourceRootResource.java | 22 +++++++------ .../v2/resources/FileObjectMapperTest.java | 6 ++-- 8 files changed, 73 insertions(+), 28 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index a461e40dea..1a132711c8 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -11,6 +11,8 @@ import org.junit.runners.Parameterized; import java.io.IOException; import java.util.Collection; +import static java.lang.Thread.sleep; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeFalse; import static sonia.scm.it.RestUtil.given; @@ -64,4 +66,34 @@ public class RepositoryAccessITCase { assertNotNull(branchName); } + + @Test + public void shouldReadContent() throws IOException, InterruptedException { + repositoryUtil.createAndCommitFile("a.txt", "a"); + + sleep(1000); + + String sourcesUrl = given() + .when() + .get(TestData.getDefaultRepositoryUrl(repositoryType)) + .then() + .statusCode(HttpStatus.SC_OK) + .extract() + .path("_links.sources.href"); + + String contentUrl = given() + .when() + .get(sourcesUrl) + .then() + .statusCode(HttpStatus.SC_OK) + .extract() + .path("files[0]._links.content.href"); + + given() + .when() + .get(contentUrl) + .then() + .statusCode(HttpStatus.SC_OK) + .body(equalTo("a")); + } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java index 6cc5eb5da9..63fabaf4e9 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java @@ -38,10 +38,8 @@ package sonia.scm.repository; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.io.Closeables; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.SVNLogEntryPath; @@ -52,20 +50,17 @@ import org.tmatesoft.svn.core.internal.util.SVNXMLUtil; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry; - import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; -//~--- JDK imports ------------------------------------------------------------ - +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; - import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +//~--- JDK imports ------------------------------------------------------------ /** * @@ -366,6 +361,7 @@ public final class SvnUtil public static long getRevisionNumber(String revision) throws RepositoryException { + // REVIEW Bei SVN wird ohne Revision die -1 genommen, was zu einem Fehler führt long revisionNumber = -1; if (Util.isNotEmpty(revision)) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java index 12c227b65c..b8ffd7ff26 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultDto.java @@ -16,6 +16,7 @@ public class BrowserResultDto extends HalRepresentation implements Iterable files; @Override @@ -24,6 +25,7 @@ public class BrowserResultDto extends HalRepresentation implements Iterable iterator() { Iterator it = null; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java index 6ab054bd74..79f049a5be 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java @@ -39,7 +39,11 @@ public class BrowserResultMapper { } private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName) { - dto.add(Links.linkingTo().self(resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision())).build()); + if (browserResult.getRevision() == null) { + dto.add(Links.linkingTo().self(resourceLinks.source().selfWithoutRevision(namespaceAndName.getNamespace(), namespaceAndName.getName())).build()); + } else { + dto.add(Links.linkingTo().self(resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision())).build()); + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index 4363a8a9b1..ebabf79342 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -25,9 +25,12 @@ public abstract class FileObjectMapper extends BaseMapper Date: Wed, 22 Aug 2018 14:29:05 +0200 Subject: [PATCH 09/21] Fix svn revision in sources HAL links --- .../sonia/scm/repository/spi/SvnBrowseCommand.java | 12 ++++++------ .../sonia/scm/api/v2/resources/FileObjectMapper.java | 8 ++++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java index 1464d7eb96..6d408c8f17 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java @@ -36,17 +36,14 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.collect.Lists; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.tmatesoft.svn.core.SVNDirEntry; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.io.SVNRepository; - import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.Repository; @@ -55,13 +52,12 @@ import sonia.scm.repository.SubRepository; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; -//~--- JDK imports ------------------------------------------------------------ - import java.io.IOException; - import java.util.Collection; import java.util.List; +//~--- JDK imports ------------------------------------------------------------ + /** * * @author Sebastian Sdorra @@ -143,6 +139,10 @@ public class SvnBrowseCommand extends AbstractSvnCommand } } + if (revisionNumber == -1) { + revisionNumber = svnRepository.getLatestRevision(); + } + result = new BrowserResult(); result.setRevision(String.valueOf(revisionNumber)); result.setFiles(children); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index ebabf79342..fbc4c8d913 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -26,11 +26,15 @@ public abstract class FileObjectMapper extends BaseMapper Date: Thu, 23 Aug 2018 09:54:36 +0200 Subject: [PATCH 10/21] Fix streaming content result for hg --- .../java/sonia/scm/api/v2/resources/ContentResource.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 0936a5b4a0..63c361538c 100644 --- 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 @@ -61,8 +61,8 @@ public class ContentResource { @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); + StreamingOutput stream = createStreamingOutput(namespace, name, revision, path); + try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Response.ResponseBuilder responseBuilder = Response.ok(stream); return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder); } catch (RepositoryNotFoundException e) { @@ -71,9 +71,9 @@ public class ContentResource { } } - private StreamingOutput createStreamingOutput(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path, RepositoryService repositoryService) { + private StreamingOutput createStreamingOutput(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) { return os -> { - try { + try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { repositoryService.getCatCommand().setRevision(revision).retriveContent(os, path); os.close(); } catch (PathNotFoundException e) { From ca563dd874b5d67a1a511d63cf78d0d58f135818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 23 Aug 2018 15:52:02 +0200 Subject: [PATCH 11/21] Fix default revision for hg and fix encoded slashes in URLs --- .../sonia/scm/it/RepositoryAccessITCase.java | 31 ++++++++++++++++--- .../java/sonia/scm/it/RepositoryUtil.java | 19 ++++++++++-- .../scm/repository/spi/HgBrowseCommand.java | 17 +++++----- .../api/v2/resources/BrowserResultMapper.java | 8 ++--- .../api/v2/resources/FileObjectMapper.java | 11 ++++--- .../v2/resources/FileObjectMapperTest.java | 4 +-- 6 files changed, 66 insertions(+), 24 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index 1a132711c8..0d0ded260e 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -70,6 +70,8 @@ public class RepositoryAccessITCase { @Test public void shouldReadContent() throws IOException, InterruptedException { repositoryUtil.createAndCommitFile("a.txt", "a"); + tempFolder.newFolder("subfolder"); + repositoryUtil.createAndCommitFile("subfolder/a.txt", "sub-a"); sleep(1000); @@ -81,19 +83,40 @@ public class RepositoryAccessITCase { .extract() .path("_links.sources.href"); - String contentUrl = given() + String rootContentUrl = given() .when() .get(sourcesUrl) .then() .statusCode(HttpStatus.SC_OK) .extract() - .path("files[0]._links.content.href"); - + .path("files.find{it.name=='a.txt'}._links.content.href"); given() .when() - .get(contentUrl) + .get(rootContentUrl) .then() .statusCode(HttpStatus.SC_OK) .body(equalTo("a")); + + String subfolderSourceUrl = given() + .when() + .get(sourcesUrl) + .then() + .statusCode(HttpStatus.SC_OK) + .extract() + .path("files.find{it.name=='subfolder'}._links.self.href"); + String subfolderContentUrl= given() + .when() + .get(subfolderSourceUrl) + .then() + .statusCode(HttpStatus.SC_OK) + .extract() + .path("files[0]._links.content.href"); + System.out.println(subfolderContentUrl); + given() + .when() + .get(subfolderContentUrl) + .then() + .statusCode(HttpStatus.SC_OK) + .body(equalTo("sub-a")); } } diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java index 98d4c8cdab..cb6d34559b 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java @@ -45,11 +45,26 @@ public class RepositoryUtil { } void createAndCommitFile(String fileName, String content) throws IOException { - Files.write(content, new File(folder, fileName), Charsets.UTF_8); - repositoryClient.getAddCommand().add(fileName); + File file = new File(folder, fileName); + Files.write(content, file, Charsets.UTF_8); + addWithParentDirectories(file); commit("added " + fileName); } + private String addWithParentDirectories(File file) throws IOException { + File parent = file.getParentFile(); + String thisName = file.getName(); + String path; + if (!folder.equals(parent)) { + addWithParentDirectories(parent); + path = addWithParentDirectories(parent) + File.separator + thisName; + } else { + path = thisName; + } + repositoryClient.getAddCommand().add(path); + return path; + } + Changeset commit(String message) throws IOException { Changeset changeset = repositoryClient.getCommitCommand().commit( new Person("scmadmin", "scmadmin@scm-manager.org"), message diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java index 0303275adf..0119b6b315 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java @@ -36,16 +36,14 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Strings; - import sonia.scm.repository.BrowserResult; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryException; import sonia.scm.repository.spi.javahg.HgFileviewCommand; -//~--- JDK imports ------------------------------------------------------------ - import java.io.IOException; +//~--- JDK imports ------------------------------------------------------------ + /** * * @author Sebastian Sdorra @@ -76,12 +74,9 @@ public class HgBrowseCommand extends AbstractCommand implements BrowseCommand * @return * * @throws IOException - * @throws RepositoryException */ @Override - public BrowserResult getBrowserResult(BrowseCommandRequest request) - throws IOException, RepositoryException - { + public BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException { HgFileviewCommand cmd = HgFileviewCommand.on(open()); if (!Strings.isNullOrEmpty(request.getRevision())) @@ -113,6 +108,12 @@ public class HgBrowseCommand extends AbstractCommand implements BrowseCommand result.setFiles(cmd.execute()); + if (!Strings.isNullOrEmpty(request.getRevision())) { + result.setRevision(request.getRevision()); + } else { + result.setRevision("tip"); + } + return result; } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java index 79f049a5be..4658a465dc 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java @@ -17,10 +17,6 @@ public class BrowserResultMapper { @Inject private ResourceLinks resourceLinks; - private FileObjectDto mapFileObject(FileObject fileObject, NamespaceAndName namespaceAndName, String revision) { - return fileObjectMapper.map(fileObject, namespaceAndName, revision); - } - public BrowserResultDto map(BrowserResult browserResult, NamespaceAndName namespaceAndName) { BrowserResultDto browserResultDto = new BrowserResultDto(); @@ -38,6 +34,10 @@ public class BrowserResultMapper { return browserResultDto; } + private FileObjectDto mapFileObject(FileObject fileObject, NamespaceAndName namespaceAndName, String revision) { + return fileObjectMapper.map(fileObject, namespaceAndName, revision); + } + private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName) { if (browserResult.getRevision() == null) { dto.add(Links.linkingTo().self(resourceLinks.source().selfWithoutRevision(namespaceAndName.getNamespace(), namespaceAndName.getName())).build()); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index fbc4c8d913..92a031b182 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -10,6 +10,7 @@ import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.SubRepository; import javax.inject.Inject; +import java.net.URI; import static de.otto.edison.hal.Link.link; @@ -25,16 +26,18 @@ public abstract class FileObjectMapper extends BaseMapper Date: Thu, 23 Aug 2018 17:02:55 +0200 Subject: [PATCH 12/21] Fix source URL creation for SVN --- .../java/sonia/scm/api/v2/resources/FileObjectMapper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java index 92a031b182..f003c236e1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java @@ -26,7 +26,7 @@ public abstract class FileObjectMapper extends BaseMapper Date: Fri, 24 Aug 2018 08:18:08 +0200 Subject: [PATCH 13/21] Harmonize naming of dto mappers --- ... => BrowserResultToBrowserResultDtoMapper.java} | 6 +++--- ...r.java => FileObjectToFileObjectDtoMapper.java} | 2 +- .../sonia/scm/api/v2/resources/MapperModule.java | 2 +- .../scm/api/v2/resources/SourceRootResource.java | 8 ++++---- ...BrowserResultToBrowserResultDtoMapperTest.java} | 10 +++++----- ...va => FileObjectToFileObjectDtoMapperTest.java} | 4 ++-- .../api/v2/resources/SourceRootResourceTest.java | 14 +++++++++----- 7 files changed, 25 insertions(+), 21 deletions(-) rename scm-webapp/src/main/java/sonia/scm/api/v2/resources/{BrowserResultMapper.java => BrowserResultToBrowserResultDtoMapper.java} (88%) rename scm-webapp/src/main/java/sonia/scm/api/v2/resources/{FileObjectMapper.java => FileObjectToFileObjectDtoMapper.java} (94%) rename scm-webapp/src/test/java/sonia/scm/api/v2/resources/{BrowserResultMapperTest.java => BrowserResultToBrowserResultDtoMapperTest.java} (89%) rename scm-webapp/src/test/java/sonia/scm/api/v2/resources/{FileObjectMapperTest.java => FileObjectToFileObjectDtoMapperTest.java} (96%) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java similarity index 88% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java index 4658a465dc..7abb1ae69b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapper.java @@ -9,10 +9,10 @@ import javax.inject.Inject; import java.util.ArrayList; import java.util.List; -public class BrowserResultMapper { +public class BrowserResultToBrowserResultDtoMapper { @Inject - private FileObjectMapper fileObjectMapper; + private FileObjectToFileObjectDtoMapper fileObjectToFileObjectDtoMapper; @Inject private ResourceLinks resourceLinks; @@ -35,7 +35,7 @@ public class BrowserResultMapper { } private FileObjectDto mapFileObject(FileObject fileObject, NamespaceAndName namespaceAndName, String revision) { - return fileObjectMapper.map(fileObject, namespaceAndName, revision); + return fileObjectToFileObjectDtoMapper.map(fileObject, namespaceAndName, revision); } private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java similarity index 94% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index f003c236e1..a3d1cae6b5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -15,7 +15,7 @@ import java.net.URI; import static de.otto.edison.hal.Link.link; @Mapper -public abstract class FileObjectMapper extends BaseMapper { +public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper { @Inject private ResourceLinks resourceLinks; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index 5ecdaee23d..4cbe6406f8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -26,7 +26,7 @@ public class MapperModule extends AbstractModule { bind(BranchToBranchDtoMapper.class).to(Mappers.getMapper(BranchToBranchDtoMapper.class).getClass()); - bind(FileObjectMapper.class).to(Mappers.getMapper(FileObjectMapper.class).getClass()); + bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass()); bind(UriInfoStore.class).in(ServletScopes.REQUEST); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index 3e0ab31ef7..4d0bfc50aa 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -20,13 +20,13 @@ import java.io.IOException; public class SourceRootResource { private final RepositoryServiceFactory serviceFactory; - private final BrowserResultMapper browserResultMapper; + private final BrowserResultToBrowserResultDtoMapper browserResultToBrowserResultDtoMapper; @Inject - public SourceRootResource(RepositoryServiceFactory serviceFactory, BrowserResultMapper browserResultMapper) { + public SourceRootResource(RepositoryServiceFactory serviceFactory, BrowserResultToBrowserResultDtoMapper browserResultToBrowserResultDtoMapper) { this.serviceFactory = serviceFactory; - this.browserResultMapper = browserResultMapper; + this.browserResultToBrowserResultDtoMapper = browserResultToBrowserResultDtoMapper; } @GET @@ -61,7 +61,7 @@ public class SourceRootResource { BrowserResult browserResult = browseCommand.getBrowserResult(); if (browserResult != null) { - return Response.ok(browserResultMapper.map(browserResult, namespaceAndName)).build(); + return Response.ok(browserResultToBrowserResultDtoMapper.map(browserResult, namespaceAndName)).build(); } else { return Response.status(Response.Status.NOT_FOUND).build(); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java similarity index 89% rename from scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java rename to scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java index 0f973d9713..cf27e35f85 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java @@ -21,17 +21,17 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; -public class BrowserResultMapperTest { +public class BrowserResultToBrowserResultDtoMapperTest { private final URI baseUri = URI.create("http://example.com/base/"); @SuppressWarnings("unused") // Is injected private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @Mock - private FileObjectMapper fileObjectMapper; + private FileObjectToFileObjectDtoMapper fileObjectToFileObjectDtoMapper; @InjectMocks - private BrowserResultMapper mapper; + private BrowserResultToBrowserResultDtoMapper mapper; private final Subject subject = mock(Subject.class); private final ThreadState subjectThreadState = new SubjectThreadState(subject); @@ -77,8 +77,8 @@ public class BrowserResultMapperTest { BrowserResultDto dto = mapper.map(browserResult, namespaceAndName); - verify(fileObjectMapper).map(fileObject1, namespaceAndName, "Revision"); - verify(fileObjectMapper).map(fileObject2, namespaceAndName, "Revision"); + verify(fileObjectToFileObjectDtoMapper).map(fileObject1, namespaceAndName, "Revision"); + verify(fileObjectToFileObjectDtoMapper).map(fileObject2, namespaceAndName, "Revision"); } private BrowserResult createBrowserResult() { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java similarity index 96% rename from scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectMapperTest.java rename to scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java index 8aa77e600d..420e99c704 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java @@ -19,14 +19,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @RunWith(MockitoJUnitRunner.Silent.class) -public class FileObjectMapperTest { +public class FileObjectToFileObjectDtoMapperTest { private final URI baseUri = URI.create("http://example.com/base/"); @SuppressWarnings("unused") // Is injected private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @InjectMocks - private FileObjectMapperImpl mapper; + private FileObjectToFileObjectDtoMapperImpl mapper; private final Subject subject = mock(Subject.class); private final ThreadState subjectThreadState = new SubjectThreadState(subject); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java index 5c1bb8c802..596618ec7e 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java @@ -10,7 +10,11 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.repository.*; +import sonia.scm.repository.BrowserResult; +import sonia.scm.repository.FileObject; +import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.RepositoryException; +import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.BrowseCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -42,10 +46,10 @@ public class SourceRootResourceTest { private BrowseCommandBuilder browseCommandBuilder; @Mock - private FileObjectMapper fileObjectMapper; + private FileObjectToFileObjectDtoMapper fileObjectToFileObjectDtoMapper; @InjectMocks - private BrowserResultMapper browserResultMapper; + private BrowserResultToBrowserResultDtoMapper browserResultToBrowserResultDtoMapper; @Before @@ -57,8 +61,8 @@ public class SourceRootResourceTest { dto.setName("name"); dto.setLength(1024); - when(fileObjectMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString())).thenReturn(dto); - SourceRootResource sourceRootResource = new SourceRootResource(serviceFactory, browserResultMapper); + when(fileObjectToFileObjectDtoMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString())).thenReturn(dto); + SourceRootResource sourceRootResource = new SourceRootResource(serviceFactory, browserResultToBrowserResultDtoMapper); RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, From 4699166f877ba6de319782600b064798a1d1b823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 24 Aug 2018 08:43:04 +0200 Subject: [PATCH 14/21] Change self link for files to content --- .../resources/FileObjectToFileObjectDtoMapper.java | 11 +++++------ .../FileObjectToFileObjectDtoMapperTest.java | 13 ++++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index a3d1cae6b5..bc814c7e0c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -12,8 +12,6 @@ import sonia.scm.repository.SubRepository; import javax.inject.Inject; import java.net.URI; -import static de.otto.edison.hal.Link.link; - @Mapper public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper { @@ -27,10 +25,11 @@ public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper Date: Fri, 24 Aug 2018 08:52:19 +0200 Subject: [PATCH 15/21] Handle empty files in content --- .../scm/api/v2/resources/ContentResource.java | 4 +++- .../api/v2/resources/ContentResourceTest.java | 22 +++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) 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 index 63c361538c..3a032aba1a 100644 --- 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 @@ -142,7 +142,9 @@ public class ContentResource { try { byte[] buffer = new byte[HEAD_BUFFER_SIZE]; int length = stream.read(buffer); - if (length < buffer.length) { + if (length < 0) { // empty file + return new byte[]{}; + } else if (length < buffer.length) { return Arrays.copyOf(buffer, length); } else { return buffer; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java index 9302a4fcfd..3d898119fb 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java @@ -108,17 +108,6 @@ public class ContentResourceTest { 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"); @@ -142,6 +131,17 @@ public class ContentResourceTest { assertTrue("stream has to be closed after reading head", stream.isClosed()); } + @Test + public void shouldHandleEmptyFile() throws Exception { + mockContent("empty", new byte[]{}); + + Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "empty"); + assertEquals(200, response.getStatus()); + + assertFalse(response.getHeaders().containsKey("Language")); + assertEquals("application/octet-stream", response.getHeaderString("Content-Type")); + } + private void mockContentFromResource(String fileName) throws Exception { URL url = Resources.getResource(fileName); mockContent(fileName, Resources.toByteArray(url)); From 0642a4c8bc346f86484740cd5b873970801347b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 24 Aug 2018 09:15:15 +0200 Subject: [PATCH 16/21] Fix integration test using self link instead of content link --- scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index 0d0ded260e..c5376a3ff2 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -89,7 +89,7 @@ public class RepositoryAccessITCase { .then() .statusCode(HttpStatus.SC_OK) .extract() - .path("files.find{it.name=='a.txt'}._links.content.href"); + .path("files.find{it.name=='a.txt'}._links.self.href"); given() .when() .get(rootContentUrl) @@ -110,7 +110,7 @@ public class RepositoryAccessITCase { .then() .statusCode(HttpStatus.SC_OK) .extract() - .path("files[0]._links.content.href"); + .path("files[0]._links.self.href"); System.out.println(subfolderContentUrl); given() .when() From 9911b3ade1a825bfaa3e6bcbcfe86e8b3186d4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 28 Aug 2018 12:43:56 +0200 Subject: [PATCH 17/21] Fix buildbreaker after merge --- .../src/test/java/sonia/scm/it/RepositoryAccessITCase.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index e3dd5c0b88..aaf0c87604 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -74,9 +74,10 @@ public class RepositoryAccessITCase { @Test public void shouldReadContent() throws IOException, InterruptedException { - repositoryUtil.createAndCommitFile("a.txt", "a"); + RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder); + RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "a.txt", "a"); tempFolder.newFolder("subfolder"); - repositoryUtil.createAndCommitFile("subfolder/a.txt", "sub-a"); + RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "subfolder/a.txt", "sub-a"); sleep(1000); From 686dd08d3c30c270152c12e881da2df092d07e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 29 Aug 2018 10:16:02 +0200 Subject: [PATCH 18/21] Unbind subjects after test --- .../BrowserResultToBrowserResultDtoMapperTest.java | 6 ++++++ .../v2/resources/FileObjectToFileObjectDtoMapperTest.java | 5 +++++ .../scm/api/v2/resources/PermissionRootResourceTest.java | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java index cf27e35f85..bb7dadb566 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BrowserResultToBrowserResultDtoMapperTest.java @@ -4,6 +4,7 @@ import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.support.SubjectThreadState; import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadState; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; @@ -61,6 +62,11 @@ public class BrowserResultToBrowserResultDtoMapperTest { fileObject2.setDirectory(true); } + @After + public void unbind() { + ThreadContext.unbindSubject(); + } + @Test public void shouldMapAttributesCorrectly() { BrowserResult browserResult = createBrowserResult(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java index 768e0d0525..23b723b748 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java @@ -4,6 +4,7 @@ import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.support.SubjectThreadState; import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadState; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +41,10 @@ public class FileObjectToFileObjectDtoMapperTest { ThreadContext.bind(subject); } + @After + public void unbind() { + ThreadContext.unbindSubject(); + } @Test public void shouldMapAttributesCorrectly() { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java index cd523cfaa8..4e1741abb5 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java @@ -18,6 +18,7 @@ import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; import org.jboss.resteasy.spi.HttpRequest; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -146,6 +147,11 @@ public class PermissionRootResourceTest { dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); } + @After + public void unbind() { + ThreadContext.unbindSubject(); + } + @TestFactory @DisplayName("test endpoints on missing repository") Stream missedRepositoryTestFactory() { From b070df4dda2bd4a1a4bdc0bbc963a0621b090ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 29 Aug 2018 10:20:14 +0200 Subject: [PATCH 19/21] Remove system out --- scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java | 1 - 1 file changed, 1 deletion(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index aaf0c87604..a7ee8da41d 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -117,7 +117,6 @@ public class RepositoryAccessITCase { .statusCode(HttpStatus.SC_OK) .extract() .path("files[0]._links.self.href"); - System.out.println(subfolderContentUrl); given() .when() .get(subfolderContentUrl) From 340859d6e271ef2cfa0bbbdd0278b01a198093e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 29 Aug 2018 10:29:22 +0200 Subject: [PATCH 20/21] Fix source layout --- .../main/java/sonia/scm/api/v2/resources/ContentResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 3a032aba1a..61fa86bf36 100644 --- 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 @@ -62,7 +62,7 @@ public class ContentResource { }) public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) { StreamingOutput stream = createStreamingOutput(namespace, name, revision, path); - try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { + try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Response.ResponseBuilder responseBuilder = Response.ok(stream); return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder); } catch (RepositoryNotFoundException e) { From 851f013013669b24c6349521154a2d04238a7987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 29 Aug 2018 14:30:23 +0200 Subject: [PATCH 21/21] Hotfix for test breaker --- .../java/sonia/scm/security/JwtAccessTokenBuilderTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java b/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java index 72aa98091b..9817389597 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/JwtAccessTokenBuilderTest.java @@ -39,6 +39,8 @@ import io.jsonwebtoken.Jwts; import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; + +import org.apache.shiro.util.ThreadContext; import org.junit.Test; import static org.junit.Assert.*; import static org.hamcrest.Matchers.*; @@ -57,6 +59,10 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class JwtAccessTokenBuilderTest { + { + ThreadContext.unbindSubject(); + } + @Mock private KeyGenerator keyGenerator;