Bootstrap v2 version to get repositories

This commit is contained in:
René Pfeuffer
2018-07-03 12:39:01 +02:00
parent ad60bae74e
commit 0768b638ed
13 changed files with 213 additions and 31 deletions

View File

@@ -37,22 +37,20 @@ import com.github.sdorra.ssp.PermissionObject;
import com.github.sdorra.ssp.StaticPermissions; import com.github.sdorra.ssp.StaticPermissions;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import sonia.scm.BasicPropertiesAware; import sonia.scm.BasicPropertiesAware;
import sonia.scm.ModelObject; import sonia.scm.ModelObject;
import sonia.scm.util.HttpUtil; import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
import sonia.scm.util.ValidationUtil; 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.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** /**
* Source code repository. * Source code repository.
@@ -207,6 +205,10 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
return name; return name;
} }
public String getNamespace() {
return namespace;
}
/** /**
* Returns the access permissions of the {@link Repository}. * Returns the access permissions of the {@link Repository}.
* *

View File

@@ -38,13 +38,11 @@ package sonia.scm.repository;
import sonia.scm.ManagerDecorator; import sonia.scm.ManagerDecorator;
import sonia.scm.Type; import sonia.scm.Type;
//~--- JDK imports ------------------------------------------------------------ import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import javax.servlet.http.HttpServletRequest; //~--- JDK imports ------------------------------------------------------------
/** /**
* Decorator for {@link RepositoryManager}. * Decorator for {@link RepositoryManager}.
@@ -92,19 +90,10 @@ public class RepositoryManagerDecorator
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/**
* {@inheritDoc}
*
*
* @param type
* @param name
*
* @return
*/
@Override @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);
} }
/** /**

View File

@@ -14,6 +14,7 @@ public class VndMediaType {
public static final String USER = PREFIX + "user" + SUFFIX; public static final String USER = PREFIX + "user" + SUFFIX;
public static final String GROUP = PREFIX + "group" + 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 USER_COLLECTION = PREFIX + "userCollection" + SUFFIX;
public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX; public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX;

View File

@@ -46,7 +46,6 @@ import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.BlameResult; import sonia.scm.repository.BlameResult;
import sonia.scm.repository.Branches; import sonia.scm.repository.Branches;
import sonia.scm.repository.BrowserResult; import sonia.scm.repository.BrowserResult;
@@ -120,19 +119,15 @@ public class RepositoryResource extends AbstractManagerResource<Repository, Repo
/** /**
* Constructs ... * Constructs ...
* *
* * @param repositoryManager
* @param configuration
* @param repositoryManager
* @param servicefactory * @param servicefactory
* @param healthChecker * @param healthChecker
*/ */
@Inject @Inject
public RepositoryResource(ScmConfiguration configuration, public RepositoryResource(RepositoryManager repositoryManager,
RepositoryManager repositoryManager,
RepositoryServiceFactory servicefactory, HealthChecker healthChecker) RepositoryServiceFactory servicefactory, HealthChecker healthChecker)
{ {
super(repositoryManager); super(repositoryManager);
this.configuration = configuration;
this.repositoryManager = repositoryManager; this.repositoryManager = repositoryManager;
this.servicefactory = servicefactory; this.servicefactory = servicefactory;
this.healthChecker = healthChecker; this.healthChecker = healthChecker;
@@ -1103,9 +1098,6 @@ public class RepositoryResource extends AbstractManagerResource<Repository, Repo
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */
private final ScmConfiguration configuration;
/** Field description */ /** Field description */
private final HealthChecker healthChecker; private final HealthChecker healthChecker;

View File

@@ -9,7 +9,7 @@ import java.time.Instant;
abstract class BaseMapper<T extends ModelObject, D extends HalRepresentation> { abstract class BaseMapper<T extends ModelObject, D extends HalRepresentation> {
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes @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) { Instant mapTime(Long epochMilli) {
return epochMilli == null? null: Instant.ofEpochMilli(epochMilli); return epochMilli == null? null: Instant.ofEpochMilli(epochMilli);

View File

@@ -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;
}

View File

@@ -15,6 +15,8 @@ public class MapperModule extends AbstractModule {
bind(GroupToGroupDtoMapper.class).to(Mappers.getMapper(GroupToGroupDtoMapper.class).getClass()); bind(GroupToGroupDtoMapper.class).to(Mappers.getMapper(GroupToGroupDtoMapper.class).getClass());
bind(GroupCollectionToDtoMapper.class); bind(GroupCollectionToDtoMapper.class);
bind(RepositoryToRepositoryDtoMapper.class).to(Mappers.getMapper(RepositoryToRepositoryDtoMapper.class).getClass());
bind(UriInfoStore.class).in(ServletScopes.REQUEST); bind(UriInfoStore.class).in(ServletScopes.REQUEST);
} }
} }

View File

@@ -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;
}

View File

@@ -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<HealthCheckFailureDto> healthCheckFailures;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Instant lastModified;
private String namespace;
private String name;
private List<PermissionDto> permissions;
@XmlElement(name = "public")
private boolean publicReadable = false;
private boolean archived = false;
private String type;
}

View File

@@ -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<Repository, RepositoryDto, RepositoryException> 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);
}
}

View File

@@ -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> repositoryResource;
@Inject
public RepositoryRootResource(Provider<RepositoryResource> repositoryResource) {
this.repositoryResource = repositoryResource;
}
@Path("{namespace}/{name}")
public RepositoryResource getRepositoryResource() {
return repositoryResource.get();
}
}

View File

@@ -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<Repository, RepositoryDto> {
@Inject
private ResourceLinks resourceLinks;
abstract HealthCheckFailureDto toDto(HealthCheckFailure failure);
abstract PermissionDto toDto(Permission permission);
}

View File

@@ -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;
}
}