diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapper.java index d0f075284f..188ad70a00 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapper.java @@ -21,17 +21,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; +import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.RepositoryType; +import sonia.scm.repository.api.Command; import javax.inject.Inject; +import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper @@ -43,6 +46,11 @@ public abstract class RepositoryTypeToRepositoryTypeDtoMapper extends BaseMapper @AfterMapping void appendLinks(RepositoryType repositoryType, @MappingTarget RepositoryTypeDto target) { Links.Builder linksBuilder = linkingTo().self(resourceLinks.repositoryType().self(repositoryType.getName())); + + if (RepositoryPermissions.create().isPermitted() && repositoryType.getSupportedCommands().contains(Command.PULL)) { + linksBuilder.single(link("importFromUrl", resourceLinks.repository().importFromUrl(repositoryType.getName()))); + } + target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java index 7bd6c353f5..e0143c83be 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java @@ -334,9 +334,11 @@ class ResourceLinks { static class RepositoryLinks { private final LinkBuilder repositoryLinkBuilder; + private final LinkBuilder repositoryImportLinkBuilder; RepositoryLinks(ScmPathInfo pathInfo) { repositoryLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryResource.class); + repositoryImportLinkBuilder = new LinkBuilder(pathInfo, RepositoryRootResource.class, RepositoryImportResource.class); } String self(String namespace, String name) { @@ -354,6 +356,10 @@ class ResourceLinks { String rename(String namespace, String name) { return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("rename").parameters().href(); } + + String importFromUrl(String type) { + return repositoryImportLinkBuilder.method("getRepositoryImportResource").parameters().method("importFromUrl").parameters(type).href(); + } } RepositoryCollectionLinks repositoryCollection() { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionToDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionToDtoMapperTest.java index 4440f0bd85..f0003c4ec1 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionToDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionToDtoMapperTest.java @@ -29,10 +29,14 @@ import com.google.common.collect.Sets; import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Link; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.After; 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.RepositoryType; @@ -50,10 +54,13 @@ public class RepositoryTypeCollectionToDtoMapperTest { @SuppressWarnings("unused") // Is injected private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); + @Mock + private Subject subject; + @InjectMocks private RepositoryTypeToRepositoryTypeDtoMapperImpl mapper; - private List types = Lists.newArrayList( + private final List types = Lists.newArrayList( new RepositoryType("hk", "Hitchhiker", Sets.newHashSet()), new RepositoryType("hog", "Heart of Gold", Sets.newHashSet()) ); @@ -63,6 +70,12 @@ public class RepositoryTypeCollectionToDtoMapperTest { @Before public void setUpEnvironment() { collectionMapper = new RepositoryTypeCollectionToDtoMapper(mapper, resourceLinks); + ThreadContext.bind(subject); + } + + @After + public void tearDown() { + ThreadContext.unbindSubject(); } @Test diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java index 2f8bce260d..880a1bec00 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeRootResourceTest.java @@ -27,8 +27,11 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.util.Providers; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,10 +58,12 @@ import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class RepositoryTypeRootResourceTest { - private RestDispatcher dispatcher = new RestDispatcher(); + private final RestDispatcher dispatcher = new RestDispatcher(); @Mock private RepositoryManager repositoryManager; + @Mock + private Subject subject; private final URI baseUri = URI.create("/"); private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @@ -66,13 +71,15 @@ public class RepositoryTypeRootResourceTest { @InjectMocks private RepositoryTypeToRepositoryTypeDtoMapperImpl mapper; - private List types = Lists.newArrayList( + private final List types = Lists.newArrayList( new RepositoryType("hk", "Hitchhiker", Sets.newHashSet()), new RepositoryType("hog", "Heart of Gold", Sets.newHashSet()) ); @Before public void prepareEnvironment() { + ThreadContext.bind(subject); + when(repositoryManager.getConfiguredTypes()).thenReturn(types); RepositoryTypeCollectionToDtoMapper collectionMapper = new RepositoryTypeCollectionToDtoMapper(mapper, resourceLinks); @@ -82,6 +89,11 @@ public class RepositoryTypeRootResourceTest { dispatcher.addSingletonResource(rootResource); } + @After + public void tearDown() { + ThreadContext.unbindSubject(); + } + @Test public void shouldHaveCollectionVndMediaType() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/" + RepositoryTypeRootResource.PATH); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapperTest.java index 74ca6e8ba2..d6a0f01079 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryTypeToRepositoryTypeDtoMapperTest.java @@ -21,19 +21,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.After; +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.RepositoryType; +import sonia.scm.repository.api.Command; import java.net.URI; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class RepositoryTypeToRepositoryTypeDtoMapperTest { @@ -43,10 +52,23 @@ public class RepositoryTypeToRepositoryTypeDtoMapperTest { @SuppressWarnings("unused") // Is injected private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); + @Mock + private Subject subject; + @InjectMocks private RepositoryTypeToRepositoryTypeDtoMapperImpl mapper; - private RepositoryType type = new RepositoryType("hk", "Hitchhiker", Sets.newHashSet()); + private final RepositoryType type = new RepositoryType("hk", "Hitchhiker", Sets.newHashSet()); + + @Before + public void init() { + ThreadContext.bind(subject); + } + + @After + public void tearDown() { + ThreadContext.unbindSubject(); + } @Test public void shouldMapSimpleProperties() { @@ -63,4 +85,31 @@ public class RepositoryTypeToRepositoryTypeDtoMapperTest { dto.getLinks().getLinkBy("self").get().getHref() ); } + + @Test + public void shouldAppendImportFromUrlLink() { + RepositoryType type = new RepositoryType("hk", "Hitchhiker", ImmutableSet.of(Command.PULL)); + when(subject.isPermitted("repository:create")).thenReturn(true); + + RepositoryTypeDto dto = mapper.map(type); + assertEquals( + "https://scm-manager.org/scm/v2/repositories/import/hk/url", + dto.getLinks().getLinkBy("importFromUrl").get().getHref() + ); + } + + @Test + public void shouldNotAppendImportFromUrlLinkIfCommandNotSupported() { + when(subject.isPermitted("repository:create")).thenReturn(true); + RepositoryTypeDto dto = mapper.map(type); + assertFalse(dto.getLinks().getLinkBy("importFromUrl").isPresent()); + } + + @Test + public void shouldNotAppendImportFromUrlLinkIfNotPermitted() { + RepositoryType type = new RepositoryType("hk", "Hitchhiker", ImmutableSet.of(Command.PULL)); + + RepositoryTypeDto dto = mapper.map(type); + assertFalse(dto.getLinks().getLinkBy("importFromUrl").isPresent()); + } }