From 0768b638edeaf7497458df1a1be034125eeed9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 3 Jul 2018 12:39:01 +0200 Subject: [PATCH] Bootstrap v2 version to get repositories --- .../java/sonia/scm/repository/Repository.java | 12 ++-- .../RepositoryManagerDecorator.java | 19 ++----- .../main/java/sonia/scm/web/VndMediaType.java | 1 + .../rest/resources/RepositoryResource.java | 12 +--- .../scm/api/v2/resources/BaseMapper.java | 2 +- .../v2/resources/HealthCheckFailureDto.java | 11 ++++ .../scm/api/v2/resources/MapperModule.java | 2 + .../scm/api/v2/resources/PermissionDto.java | 11 ++++ .../scm/api/v2/resources/RepositoryDto.java | 29 ++++++++++ .../api/v2/resources/RepositoryResource.java | 44 +++++++++++++++ .../v2/resources/RepositoryRootResource.java | 25 +++++++++ .../RepositoryToRepositoryDtoMapper.java | 20 +++++++ .../RepositoryToRepositoryDtoMapperTest.java | 56 +++++++++++++++++++ 13 files changed, 213 insertions(+), 31 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/HealthCheckFailureDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRootResource.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java diff --git a/scm-core/src/main/java/sonia/scm/repository/Repository.java b/scm-core/src/main/java/sonia/scm/repository/Repository.java index 63bef4ea6c..0b4d1c4dcd 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Repository.java +++ b/scm-core/src/main/java/sonia/scm/repository/Repository.java @@ -37,22 +37,20 @@ import com.github.sdorra.ssp.PermissionObject; import com.github.sdorra.ssp.StaticPermissions; import com.google.common.base.Objects; import com.google.common.collect.Lists; - import sonia.scm.BasicPropertiesAware; import sonia.scm.ModelObject; import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; import sonia.scm.util.ValidationUtil; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Source code repository. @@ -207,6 +205,10 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per return name; } + public String getNamespace() { + return namespace; + } + /** * Returns the access permissions of the {@link Repository}. * diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java index 632b54b741..d99a6483ab 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java @@ -38,13 +38,11 @@ package sonia.scm.repository; import sonia.scm.ManagerDecorator; import sonia.scm.Type; -//~--- JDK imports ------------------------------------------------------------ - +import javax.servlet.http.HttpServletRequest; import java.io.IOException; - import java.util.Collection; -import javax.servlet.http.HttpServletRequest; +//~--- JDK imports ------------------------------------------------------------ /** * Decorator for {@link RepositoryManager}. @@ -92,19 +90,10 @@ public class RepositoryManagerDecorator //~--- get methods ---------------------------------------------------------- - /** - * {@inheritDoc} - * - * - * @param type - * @param name - * - * @return - */ @Override - public Repository get(String type, String name) + public Repository get(String namespace, String name) { - return decorated.get(type, name); + return decorated.get(namespace, name); } /** 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 3ec121f9a4..f306114ecb 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -14,6 +14,7 @@ public class VndMediaType { public static final String USER = PREFIX + "user" + SUFFIX; public static final String GROUP = PREFIX + "group" + SUFFIX; + public static final String REPOSITORY = PREFIX + "repository" + SUFFIX; public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX; public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java index 0637f3fc8f..740f841f18 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java @@ -46,7 +46,6 @@ import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.AuthorizationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.BlameResult; import sonia.scm.repository.Branches; import sonia.scm.repository.BrowserResult; @@ -120,19 +119,15 @@ public class RepositoryResource extends AbstractManagerResource { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes - public abstract D map(T user); + public abstract D map(T modelObject); Instant mapTime(Long epochMilli) { return epochMilli == null? null: Instant.ofEpochMilli(epochMilli); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/HealthCheckFailureDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/HealthCheckFailureDto.java new file mode 100644 index 0000000000..21d98c257c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/HealthCheckFailureDto.java @@ -0,0 +1,11 @@ +package sonia.scm.api.v2.resources; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter +public class HealthCheckFailureDto { + private String description; + private String summary; + private String url; +} 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 fec2a677b5..0a0468de3c 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 @@ -15,6 +15,8 @@ public class MapperModule extends AbstractModule { bind(GroupToGroupDtoMapper.class).to(Mappers.getMapper(GroupToGroupDtoMapper.class).getClass()); bind(GroupCollectionToDtoMapper.class); + bind(RepositoryToRepositoryDtoMapper.class).to(Mappers.getMapper(RepositoryToRepositoryDtoMapper.class).getClass()); + bind(UriInfoStore.class).in(ServletScopes.REQUEST); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java new file mode 100644 index 0000000000..293a16dd17 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java @@ -0,0 +1,11 @@ +package sonia.scm.api.v2.resources; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter +public class PermissionDto { + private String type; + private String name; + private boolean groupPermission; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java new file mode 100644 index 0000000000..6afac172df --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java @@ -0,0 +1,29 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.HalRepresentation; +import lombok.Getter; +import lombok.Setter; + +import javax.xml.bind.annotation.XmlElement; +import java.time.Instant; +import java.util.List; + +@Getter @Setter +public class RepositoryDto extends HalRepresentation { + + private String id; + private String contact; + private Instant creationDate; + private String description; + private List healthCheckFailures; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Instant lastModified; + private String namespace; + private String name; + private List permissions; + @XmlElement(name = "public") + private boolean publicReadable = false; + private boolean archived = false; + private String type; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java new file mode 100644 index 0000000000..c171dc5d83 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java @@ -0,0 +1,44 @@ +package sonia.scm.api.v2.resources; + +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryException; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +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; + +public class RepositoryResource { + + private final RepositoryToRepositoryDtoMapper repositoryToDtoMapper; + + private final ResourceManagerAdapter adapter; + + @Inject + public RepositoryResource(RepositoryToRepositoryDtoMapper repositoryToDtoMapper, RepositoryManager manager) { + this.repositoryToDtoMapper = repositoryToDtoMapper; + this.adapter = new ResourceManagerAdapter<>(manager); + } + + @GET + @Path("") + @Produces(VndMediaType.REPOSITORY) + @TypeHint(RepositoryDto.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), + @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the repository"), + @ResponseCode(code = 404, condition = "not found, no repository with the specified name available in the namespace"), + @ResponseCode(code = 500, condition = "internal server error") + }) + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name) { + return adapter.get("31QwjAKOK2", repositoryToDtoMapper::map); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRootResource.java new file mode 100644 index 0000000000..e7861a884b --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRootResource.java @@ -0,0 +1,25 @@ +package sonia.scm.api.v2.resources; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.ws.rs.Path; + +/** + * RESTful Web Service Resource to manage repositories. + */ +@Path(RepositoryRootResource.REPOSITORIES_PATH_V2) +public class RepositoryRootResource { + static final String REPOSITORIES_PATH_V2 = "v2/repositories/"; + + private final Provider repositoryResource; + + @Inject + public RepositoryRootResource(Provider repositoryResource) { + this.repositoryResource = repositoryResource; + } + + @Path("{namespace}/{name}") + public RepositoryResource getRepositoryResource() { + return repositoryResource.get(); + } +} 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 new file mode 100644 index 0000000000..0574fa9dd6 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -0,0 +1,20 @@ +package sonia.scm.api.v2.resources; + +import com.google.inject.Inject; +import org.mapstruct.Mapper; +import sonia.scm.repository.HealthCheckFailure; +import sonia.scm.repository.Permission; +import sonia.scm.repository.Repository; + +// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. +@SuppressWarnings("squid:S3306") +@Mapper +public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper { + + @Inject + private ResourceLinks resourceLinks; + + abstract HealthCheckFailureDto toDto(HealthCheckFailure failure); + + abstract PermissionDto toDto(Permission permission); +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java new file mode 100644 index 0000000000..39eb8418a6 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java @@ -0,0 +1,56 @@ +package sonia.scm.api.v2.resources; + +import org.junit.Test; +import sonia.scm.repository.HealthCheckFailure; +import sonia.scm.repository.Permission; +import sonia.scm.repository.PermissionType; +import sonia.scm.repository.Repository; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + +public class RepositoryToRepositoryDtoMapperTest { + + private RepositoryToRepositoryDtoMapperImpl mapper = new RepositoryToRepositoryDtoMapperImpl(); + + @Test + public void shouldMapSimpleProperties() { + RepositoryDto dto = mapper.map(createDummyRepository()); + assertEquals("namespace", dto.getNamespace()); + assertEquals("name", dto.getName()); + assertEquals("description", dto.getDescription()); + assertEquals("git", dto.getType()); + assertEquals("none@example.com", dto.getContact()); + assertEquals("1", dto.getId()); + } + + @Test + public void shouldMapHealthCheck() { + RepositoryDto dto = mapper.map(createDummyRepository()); + assertEquals(1, dto.getHealthCheckFailures().size()); + assertEquals("summary", dto.getHealthCheckFailures().get(0).getSummary()); + } + + @Test + public void shouldMapPermissions() { + RepositoryDto dto = mapper.map(createDummyRepository()); + assertEquals(1, dto.getPermissions().size()); + assertEquals("permission", dto.getPermissions().get(0).getName()); + assertEquals("READ", dto.getPermissions().get(0).getType()); + } + + private Repository createDummyRepository() { + Repository repository = new Repository(); + repository.setNamespace("namespace"); + repository.setName("name"); + repository.setDescription("description"); + repository.setType("git"); + repository.setContact("none@example.com"); + repository.setId("1"); + repository.setCreationDate(System.currentTimeMillis()); + repository.setHealthCheckFailures(asList(new HealthCheckFailure("1", "summary", "url", "failure"))); + repository.setPermissions(asList(new Permission("permission", PermissionType.READ))); + + return repository; + } +}