mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 07:55:47 +01:00
Merging base branch back into feature
This commit is contained in:
@@ -53,13 +53,9 @@ public interface HandlerBase<T extends TypedObject, E extends Exception>
|
||||
/**
|
||||
* Persists a new object.
|
||||
*
|
||||
*
|
||||
* @param object to store
|
||||
*
|
||||
* @throws E
|
||||
* @throws IOException
|
||||
* @return The persisted object.
|
||||
*/
|
||||
public void create(T object) throws E;
|
||||
public T create(T object) throws E;
|
||||
|
||||
/**
|
||||
* Removes a persistent object.
|
||||
|
||||
@@ -77,9 +77,9 @@ public class ManagerDecorator<T extends ModelObject, E extends Exception>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void create(T object) throws E
|
||||
public T create(T object) throws E
|
||||
{
|
||||
decorated.create(object);
|
||||
return decorated.create(object);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,7 +80,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends SimpleRepository
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(Repository repository)
|
||||
public Repository create(Repository repository)
|
||||
throws RepositoryException {
|
||||
File directory = getDirectory(repository);
|
||||
|
||||
@@ -94,6 +94,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends SimpleRepository
|
||||
fileSystem.create(directory);
|
||||
create(repository, directory);
|
||||
postCreate(repository, directory);
|
||||
return repository;
|
||||
} catch (Exception ex) {
|
||||
if (directory.exists()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -109,6 +110,8 @@ public abstract class AbstractSimpleRepositoryHandler<C extends SimpleRepository
|
||||
}
|
||||
|
||||
Throwables.propagateIfPossible(ex, RepositoryException.class);
|
||||
// This point will never be reached
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import sonia.scm.TypeManager;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
@@ -134,4 +135,11 @@ public interface RepositoryManager
|
||||
*/
|
||||
@Override
|
||||
public RepositoryHandler getHandler(String type);
|
||||
|
||||
default Optional<Repository> getByNamespace(String namespace, String name) {
|
||||
return getAll()
|
||||
.stream()
|
||||
.filter(r -> r.getName().equals(name) && r.getNamespace().equals(namespace))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ 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;
|
||||
public static final String REPOSITORY_COLLECTION = PREFIX + "repositoryCollection" + SUFFIX;
|
||||
|
||||
private VndMediaType() {
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public class ManagerTest {
|
||||
public Collection getAll(Comparator comparator, int start, int limit) { return null; }
|
||||
|
||||
@Override
|
||||
public void create(TypedObject object) {}
|
||||
public TypedObject create(TypedObject object) { return null; }
|
||||
|
||||
@Override
|
||||
public void delete(TypedObject object) {}
|
||||
|
||||
@@ -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<Repository, Repo
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param configuration
|
||||
* @param repositoryManager
|
||||
* @param repositoryManager
|
||||
* @param servicefactory
|
||||
* @param healthChecker
|
||||
*/
|
||||
@Inject
|
||||
public RepositoryResource(ScmConfiguration configuration,
|
||||
RepositoryManager repositoryManager,
|
||||
public RepositoryResource(RepositoryManager repositoryManager,
|
||||
RepositoryServiceFactory servicefactory, HealthChecker healthChecker)
|
||||
{
|
||||
super(repositoryManager, Repository.class);
|
||||
this.configuration = configuration;
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.servicefactory = servicefactory;
|
||||
this.healthChecker = healthChecker;
|
||||
@@ -1067,9 +1062,6 @@ public class RepositoryResource extends AbstractManagerResource<Repository, Repo
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** Field description */
|
||||
private final HealthChecker healthChecker;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.time.Instant;
|
||||
abstract class BaseMapper<T extends ModelObject, D extends HalRepresentation> {
|
||||
|
||||
@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);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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 javax.ws.rs.core.Response;
|
||||
|
||||
public class BranchCollectionResource {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
public class BranchRootResource {
|
||||
|
||||
private final Provider<BranchCollectionResource> branchCollectionResource;
|
||||
|
||||
@Inject
|
||||
public BranchRootResource(Provider<BranchCollectionResource> branchCollectionResource) {
|
||||
this.branchCollectionResource = branchCollectionResource;
|
||||
}
|
||||
|
||||
@Path("")
|
||||
public BranchCollectionResource getBranchCollectionResource() {
|
||||
return branchCollectionResource.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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 javax.ws.rs.core.Response;
|
||||
|
||||
public class ChangesetCollectionResource {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
public class ChangesetRootResource {
|
||||
|
||||
private final Provider<ChangesetCollectionResource> changesetCollectionResource;
|
||||
|
||||
@Inject
|
||||
public ChangesetRootResource(Provider<ChangesetCollectionResource> changesetCollectionResource) {
|
||||
this.changesetCollectionResource = changesetCollectionResource;
|
||||
}
|
||||
|
||||
@Path("")
|
||||
public ChangesetCollectionResource getChangesetCollectionResource() {
|
||||
return changesetCollectionResource.get();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import sonia.scm.api.rest.resources.AbstractManagerResource;
|
||||
|
||||
import javax.ws.rs.core.GenericEntity;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
@@ -17,52 +16,25 @@ import java.util.function.Supplier;
|
||||
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* Adapter from resource http endpoints to managers.
|
||||
* Adapter from resource http endpoints to managers, for Collection resources (e.g. {@code /users}).
|
||||
*
|
||||
* Provides common CRUD operations and DTO to Model Object mapping to keep Resources more DRY.
|
||||
*
|
||||
* @param <MODEL_OBJECT> The type of the model object, eg. {@link sonia.scm.user.User}.
|
||||
* @param <DTO> The corresponding transport object, eg. {@link UserDto}.
|
||||
* @param <EXCEPTION> The exception type for the model object, eg. {@link sonia.scm.user.UserException}.
|
||||
*
|
||||
* @see SingleResourceManagerAdapter
|
||||
*/
|
||||
@SuppressWarnings("squid:S00119") // "MODEL_OBJECT" is much more meaningful than "M", right?
|
||||
class ResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
class CollectionResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
DTO extends HalRepresentation,
|
||||
EXCEPTION extends Exception> extends AbstractManagerResource<MODEL_OBJECT, EXCEPTION> {
|
||||
|
||||
ResourceManagerAdapter(Manager<MODEL_OBJECT, EXCEPTION> manager, Class<MODEL_OBJECT> type) {
|
||||
CollectionResourceManagerAdapter(Manager<MODEL_OBJECT, EXCEPTION> manager, Class<MODEL_OBJECT> type) {
|
||||
super(manager, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the model object for the given id, transforms it to a dto and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. no matching object for the id or missing privileges.
|
||||
*/
|
||||
Response get(String id, Function<MODEL_OBJECT, DTO> mapToDto) {
|
||||
MODEL_OBJECT modelObject = manager.get(id);
|
||||
if (modelObject == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
DTO dto = mapToDto.apply(modelObject);
|
||||
return Response.ok(dto).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the model object for the given id according to the given function and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. no matching object for the id or missing privileges.
|
||||
*/
|
||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) {
|
||||
MODEL_OBJECT existingModelObject = manager.get(id);
|
||||
if (existingModelObject == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject);
|
||||
if (!id.equals(changedModelObject.getId())) {
|
||||
return Response.status(BAD_REQUEST).entity("illegal change of id").build();
|
||||
}
|
||||
return update(id, changedModelObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all model objects in a paged way, maps them using the given function and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. missing privileges.
|
||||
@@ -76,13 +48,13 @@ class ResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
* Creates a model object for the given dto and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. no conflicts or missing privileges.
|
||||
*/
|
||||
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) throws IOException, EXCEPTION {
|
||||
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) throws EXCEPTION {
|
||||
if (dto == null) {
|
||||
return Response.status(BAD_REQUEST).build();
|
||||
}
|
||||
MODEL_OBJECT modelObject = modelObjectSupplier.get();
|
||||
manager.create(modelObject);
|
||||
return Response.created(URI.create(uriCreator.apply(modelObject))).build();
|
||||
MODEL_OBJECT created = manager.create(modelObject);
|
||||
return Response.created(URI.create(uriCreator.apply(created))).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,14 +29,14 @@ public class GroupCollectionResource {
|
||||
private final GroupCollectionToDtoMapper groupCollectionToDtoMapper;
|
||||
private final ResourceLinks resourceLinks;
|
||||
|
||||
private final ResourceManagerAdapter<Group, GroupDto, GroupException> adapter;
|
||||
private final IdResourceManagerAdapter<Group, GroupDto, GroupException> adapter;
|
||||
|
||||
@Inject
|
||||
public GroupCollectionResource(GroupManager manager, GroupDtoToGroupMapper dtoToGroupMapper, GroupCollectionToDtoMapper groupCollectionToDtoMapper, ResourceLinks resourceLinks) {
|
||||
this.dtoToGroupMapper = dtoToGroupMapper;
|
||||
this.groupCollectionToDtoMapper = groupCollectionToDtoMapper;
|
||||
this.resourceLinks = resourceLinks;
|
||||
this.adapter = new ResourceManagerAdapter<>(manager, Group.class);
|
||||
this.adapter = new IdResourceManagerAdapter<>(manager, Group.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,14 +22,14 @@ public class GroupResource {
|
||||
|
||||
private final GroupToGroupDtoMapper groupToGroupDtoMapper;
|
||||
private final GroupDtoToGroupMapper dtoToGroupMapper;
|
||||
private final ResourceManagerAdapter<Group, GroupDto, GroupException> adapter;
|
||||
private final IdResourceManagerAdapter<Group, GroupDto, GroupException> adapter;
|
||||
|
||||
@Inject
|
||||
public GroupResource(GroupManager manager, GroupToGroupDtoMapper groupToGroupDtoMapper,
|
||||
GroupDtoToGroupMapper groupDtoToGroupMapper) {
|
||||
this.groupToGroupDtoMapper = groupToGroupDtoMapper;
|
||||
this.dtoToGroupMapper = groupDtoToGroupMapper;
|
||||
this.adapter = new ResourceManagerAdapter<>(manager, Group.class);
|
||||
this.adapter = new IdResourceManagerAdapter<>(manager, Group.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import sonia.scm.Manager;
|
||||
import sonia.scm.ModelObject;
|
||||
import sonia.scm.PageResult;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Facade for {@link SingleResourceManagerAdapter} and {@link CollectionResourceManagerAdapter}
|
||||
* for model objects handled by a single id.
|
||||
*/
|
||||
@SuppressWarnings("squid:S00119") // "MODEL_OBJECT" is much more meaningful than "M", right?
|
||||
class IdResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
DTO extends HalRepresentation,
|
||||
EXCEPTION extends Exception> {
|
||||
|
||||
private final Manager<MODEL_OBJECT, EXCEPTION> manager;
|
||||
|
||||
private final SingleResourceManagerAdapter<MODEL_OBJECT, DTO, EXCEPTION> singleAdapter;
|
||||
private final CollectionResourceManagerAdapter<MODEL_OBJECT, DTO, EXCEPTION> collectionAdapter;
|
||||
|
||||
IdResourceManagerAdapter(Manager<MODEL_OBJECT, EXCEPTION> manager, Class<MODEL_OBJECT> type) {
|
||||
this.manager = manager;
|
||||
singleAdapter = new SingleResourceManagerAdapter<>(manager, type);
|
||||
collectionAdapter = new CollectionResourceManagerAdapter<>(manager, type);
|
||||
}
|
||||
|
||||
Response get(String id, Function<MODEL_OBJECT, DTO> mapToDto) {
|
||||
return singleAdapter.get(loadBy(id), mapToDto);
|
||||
}
|
||||
|
||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) {
|
||||
return singleAdapter.update(
|
||||
loadBy(id),
|
||||
applyChanges,
|
||||
idStaysTheSame(id)
|
||||
);
|
||||
}
|
||||
|
||||
public Response getAll(int page, int pageSize, String sortBy, boolean desc, Function<PageResult<MODEL_OBJECT>, CollectionDto> mapToDto) {
|
||||
return collectionAdapter.getAll(page, pageSize, sortBy, desc, mapToDto);
|
||||
}
|
||||
|
||||
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) throws IOException, EXCEPTION {
|
||||
return collectionAdapter.create(dto, modelObjectSupplier, uriCreator);
|
||||
}
|
||||
|
||||
public Response delete(String id) {
|
||||
return singleAdapter.delete(id);
|
||||
}
|
||||
|
||||
private Supplier<Optional<MODEL_OBJECT>> loadBy(String id) {
|
||||
return () -> Optional.ofNullable(manager.get(id));
|
||||
}
|
||||
|
||||
private Predicate<MODEL_OBJECT> idStaysTheSame(String id) {
|
||||
return changed -> changed.getId().equals(id);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ 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(RepositoryDtoToRepositoryMapper.class).to(Mappers.getMapper(RepositoryDtoToRepositoryMapper.class).getClass());
|
||||
|
||||
bind(UriInfoStore.class).in(ServletScopes.REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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 javax.ws.rs.core.Response;
|
||||
|
||||
public class PermissionCollectionResource {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
public class PermissionRootResource {
|
||||
|
||||
private final Provider<PermissionCollectionResource> permissionCollectionResource;
|
||||
|
||||
@Inject
|
||||
public PermissionRootResource(Provider<PermissionCollectionResource> permissionCollectionResource) {
|
||||
this.permissionCollectionResource = permissionCollectionResource;
|
||||
}
|
||||
|
||||
@Path("")
|
||||
public PermissionCollectionResource getPermissionCollectionResource() {
|
||||
return permissionCollectionResource.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
||||
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.Consumes;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
public class RepositoryCollectionResource {
|
||||
|
||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
private final CollectionResourceManagerAdapter<Repository, RepositoryDto, RepositoryException> adapter;
|
||||
private final RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper;
|
||||
private final RepositoryDtoToRepositoryMapper dtoToRepositoryMapper;
|
||||
private final ResourceLinks resourceLinks;
|
||||
|
||||
@Inject
|
||||
public RepositoryCollectionResource(RepositoryManager manager, RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper, RepositoryDtoToRepositoryMapper dtoToRepositoryMapper, ResourceLinks resourceLinks) {
|
||||
this.adapter = new CollectionResourceManagerAdapter<>(manager, Repository.class);
|
||||
this.repositoryCollectionToDtoMapper = repositoryCollectionToDtoMapper;
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.resourceLinks = resourceLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all repositories for a given page number with a given page size (default page size is {@value DEFAULT_PAGE_SIZE}).
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param page the number of the requested page
|
||||
* @param pageSize the page size (default page size is {@value DEFAULT_PAGE_SIZE})
|
||||
* @param sortBy sort parameter (if empty - undefined sorting)
|
||||
* @param desc sort direction desc or asc
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces(VndMediaType.REPOSITORY_COLLECTION)
|
||||
@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 does not have the \"repository\" privilege"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
|
||||
@DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize,
|
||||
@QueryParam("sortBy") String sortBy,
|
||||
@DefaultValue("false") @QueryParam("desc") boolean desc) {
|
||||
return adapter.getAll(page, pageSize, sortBy, desc,
|
||||
pageResult -> repositoryCollectionToDtoMapper.map(page, pageSize, pageResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new repository.
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege. The namespace of the given repository will
|
||||
* be ignored and set by the configured namespace strategy.
|
||||
*
|
||||
* @param repositoryDto The repository to be created.
|
||||
* @return A response with the link to the new repository (if created successfully).
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
@Consumes(VndMediaType.REPOSITORY)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 201, condition = "create success"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"),
|
||||
@ResponseCode(code = 409, condition = "conflict, a repository with this name already exists"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository"))
|
||||
public Response create(RepositoryDto repositoryDto) throws RepositoryException {
|
||||
return adapter.create(repositoryDto,
|
||||
() -> dtoToRepositoryMapper.map(repositoryDto, null),
|
||||
repository -> resourceLinks.repository().self(repository.getNamespace(), repository.getName()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||
@SuppressWarnings("squid:S3306")
|
||||
public class RepositoryCollectionToDtoMapper extends BasicCollectionToDtoMapper<Repository, RepositoryDto> {
|
||||
|
||||
private final ResourceLinks resourceLinks;
|
||||
|
||||
@Inject
|
||||
public RepositoryCollectionToDtoMapper(RepositoryToRepositoryDtoMapper repositoryToDtoMapper, ResourceLinks resourceLinks) {
|
||||
super("repositories", repositoryToDtoMapper);
|
||||
this.resourceLinks = resourceLinks;
|
||||
}
|
||||
|
||||
@Override
|
||||
String createCreateLink() {
|
||||
return resourceLinks.repositoryCollection().create();
|
||||
}
|
||||
|
||||
@Override
|
||||
String createSelfLink() {
|
||||
return resourceLinks.repositoryCollection().self();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCreatePermitted() {
|
||||
return RepositoryPermissions.create().isPermitted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter @Setter
|
||||
public class RepositoryDto extends HalRepresentation {
|
||||
|
||||
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 boolean archived = false;
|
||||
private String type;
|
||||
protected Map<String, String> properties;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||
protected HalRepresentation add(Links links) {
|
||||
return super.add(links);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Context;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
@Mapper
|
||||
public abstract class RepositoryDtoToRepositoryMapper {
|
||||
|
||||
@Mapping(target = "creationDate", ignore = true)
|
||||
@Mapping(target = "lastModified", ignore = true)
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "publicReadable", ignore = true)
|
||||
@Mapping(target = "healthCheckFailures", ignore = true)
|
||||
@Mapping(target = "permissions", ignore = true)
|
||||
public abstract Repository map(RepositoryDto repositoryDto, @Context String id);
|
||||
|
||||
@AfterMapping
|
||||
void updateId(@MappingTarget Repository repository, @Context String id) {
|
||||
repository.setId(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
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.inject.Provider;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class RepositoryResource {
|
||||
|
||||
private final RepositoryToRepositoryDtoMapper repositoryToDtoMapper;
|
||||
private final RepositoryDtoToRepositoryMapper dtoToRepositoryMapper;
|
||||
|
||||
private final RepositoryManager manager;
|
||||
private final SingleResourceManagerAdapter<Repository, RepositoryDto, RepositoryException> adapter;
|
||||
private final Provider<TagRootResource> tagRootResource;
|
||||
private final Provider<BranchRootResource> branchRootResource;
|
||||
private final Provider<ChangesetRootResource> changesetRootResource;
|
||||
private final Provider<SourceRootResource> sourceRootResource;
|
||||
private final Provider<PermissionRootResource> permissionRootResource;
|
||||
|
||||
@Inject
|
||||
public RepositoryResource(
|
||||
RepositoryToRepositoryDtoMapper repositoryToDtoMapper,
|
||||
RepositoryDtoToRepositoryMapper dtoToRepositoryMapper, RepositoryManager manager,
|
||||
Provider<TagRootResource> tagRootResource,
|
||||
Provider<BranchRootResource> branchRootResource,
|
||||
Provider<ChangesetRootResource> changesetRootResource,
|
||||
Provider<SourceRootResource> sourceRootResource, Provider<PermissionRootResource> permissionRootResource) {
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.manager = manager;
|
||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||
this.adapter = new SingleResourceManagerAdapter<>(manager, Repository.class);
|
||||
this.tagRootResource = tagRootResource;
|
||||
this.branchRootResource = branchRootResource;
|
||||
this.changesetRootResource = changesetRootResource;
|
||||
this.sourceRootResource = sourceRootResource;
|
||||
this.permissionRootResource = permissionRootResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a repository.
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository
|
||||
* @param name the name of the repository
|
||||
*
|
||||
*/
|
||||
@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(loadBy(namespace, name), repositoryToDtoMapper::map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a repository.
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository to delete
|
||||
* @param name the name of the repository to delete
|
||||
*
|
||||
*/
|
||||
@DELETE
|
||||
@Path("")
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 204, condition = "delete success or nothing to delete"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
public Response delete(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
||||
return adapter.delete(loadBy(namespace, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the given repository.
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository to be modified
|
||||
* @param name the name of the repository to be modified
|
||||
* @param repositoryDto repository object to modify
|
||||
*/
|
||||
@PUT
|
||||
@Path("")
|
||||
@Consumes(VndMediaType.REPOSITORY)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 204, condition = "update success"),
|
||||
@ResponseCode(code = 400, condition = "Invalid body, e.g. illegal change of namespace or name"),
|
||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"),
|
||||
@ResponseCode(code = 404, condition = "not found, no repository with the specified namespace and name available"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, RepositoryDto repositoryDto) {
|
||||
return adapter.update(
|
||||
loadBy(namespace, name),
|
||||
existing -> dtoToRepositoryMapper.map(repositoryDto, existing.getId()),
|
||||
nameAndNamespaceStaysTheSame(namespace, name)
|
||||
);
|
||||
}
|
||||
|
||||
@Path("tags/")
|
||||
public TagRootResource tags() {
|
||||
return tagRootResource.get();
|
||||
}
|
||||
|
||||
@Path("branches/")
|
||||
public BranchRootResource branches() {
|
||||
return branchRootResource.get();
|
||||
}
|
||||
|
||||
@Path("changesets/")
|
||||
public ChangesetRootResource changesets() {
|
||||
return changesetRootResource.get();
|
||||
}
|
||||
|
||||
@Path("sources/")
|
||||
public SourceRootResource sources() {
|
||||
return sourceRootResource.get();
|
||||
}
|
||||
|
||||
@Path("permissions/")
|
||||
public PermissionRootResource permissions() {
|
||||
return permissionRootResource.get();
|
||||
}
|
||||
|
||||
private Supplier<Optional<Repository>> loadBy(String namespace, String name) {
|
||||
return () -> manager.getByNamespace(namespace, name);
|
||||
}
|
||||
|
||||
private Predicate<Repository> nameAndNamespaceStaysTheSame(String namespace, String name) {
|
||||
return changed -> changed.getName().equals(name) && changed.getNamespace().equals(namespace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
private final Provider<RepositoryCollectionResource> repositoryCollectionResource;
|
||||
|
||||
@Inject
|
||||
public RepositoryRootResource(Provider<RepositoryResource> repositoryResource, Provider<RepositoryCollectionResource> repositoryCollectionResource) {
|
||||
this.repositoryResource = repositoryResource;
|
||||
this.repositoryCollectionResource = repositoryCollectionResource;
|
||||
}
|
||||
|
||||
@Path("{namespace}/{name}")
|
||||
public RepositoryResource getRepositoryResource() {
|
||||
return repositoryResource.get();
|
||||
}
|
||||
|
||||
@Path("")
|
||||
public RepositoryCollectionResource getRepositoryCollectionResource() {
|
||||
return repositoryCollectionResource.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import de.otto.edison.hal.Links;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import sonia.scm.repository.HealthCheckFailure;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
|
||||
import static de.otto.edison.hal.Link.link;
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
// 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);
|
||||
|
||||
@AfterMapping
|
||||
void appendLinks(Repository repository, @MappingTarget RepositoryDto target) {
|
||||
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(target.getNamespace(), target.getName()));
|
||||
if (RepositoryPermissions.delete(repository).isPermitted()) {
|
||||
linksBuilder.single(link("delete", resourceLinks.repository().delete(target.getNamespace(), target.getName())));
|
||||
}
|
||||
if (RepositoryPermissions.modify(repository).isPermitted()) {
|
||||
linksBuilder.single(link("update", resourceLinks.repository().update(target.getNamespace(), target.getName())));
|
||||
linksBuilder.single(link("permissions", resourceLinks.permissionCollection().self(target.getNamespace(), target.getName())));
|
||||
}
|
||||
linksBuilder.single(link("tags", resourceLinks.tagCollection().self(target.getNamespace(), target.getName())));
|
||||
linksBuilder.single(link("branches", resourceLinks.branchCollection().self(target.getNamespace(), target.getName())));
|
||||
linksBuilder.single(link("changesets", resourceLinks.changesetCollection().self(target.getNamespace(), target.getName())));
|
||||
linksBuilder.single(link("sources", resourceLinks.sourceCollection().self(target.getNamespace(), target.getName())));
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class ResourceLinks {
|
||||
static class GroupLinks {
|
||||
private final LinkBuilder groupLinkBuilder;
|
||||
|
||||
private GroupLinks(UriInfo uriInfo) {
|
||||
GroupLinks(UriInfo uriInfo) {
|
||||
groupLinkBuilder = new LinkBuilder(uriInfo, GroupRootResource.class, GroupResource.class);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class ResourceLinks {
|
||||
static class GroupCollectionLinks {
|
||||
private final LinkBuilder collectionLinkBuilder;
|
||||
|
||||
private GroupCollectionLinks(UriInfo uriInfo) {
|
||||
GroupCollectionLinks(UriInfo uriInfo) {
|
||||
collectionLinkBuilder = new LinkBuilder(uriInfo, GroupRootResource.class, GroupCollectionResource.class);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class ResourceLinks {
|
||||
static class UserLinks {
|
||||
private final LinkBuilder userLinkBuilder;
|
||||
|
||||
private UserLinks(UriInfo uriInfo) {
|
||||
UserLinks(UriInfo uriInfo) {
|
||||
userLinkBuilder = new LinkBuilder(uriInfo, UserRootResource.class, UserResource.class);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class ResourceLinks {
|
||||
static class UserCollectionLinks {
|
||||
private final LinkBuilder collectionLinkBuilder;
|
||||
|
||||
private UserCollectionLinks(UriInfo uriInfo) {
|
||||
UserCollectionLinks(UriInfo uriInfo) {
|
||||
collectionLinkBuilder = new LinkBuilder(uriInfo, UserRootResource.class, UserCollectionResource.class);
|
||||
}
|
||||
|
||||
@@ -100,4 +100,128 @@ class ResourceLinks {
|
||||
return collectionLinkBuilder.method("getUserCollectionResource").parameters().method("create").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public RepositoryLinks repository() {
|
||||
return new RepositoryLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class RepositoryLinks {
|
||||
private final LinkBuilder repositoryLinkBuilder;
|
||||
|
||||
RepositoryLinks(UriInfo uriInfo) {
|
||||
repositoryLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class);
|
||||
}
|
||||
|
||||
String self(String namespace, String name) {
|
||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("get").parameters().href();
|
||||
}
|
||||
|
||||
String delete(String namespace, String name) {
|
||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("delete").parameters().href();
|
||||
}
|
||||
|
||||
String update(String namespace, String name) {
|
||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("update").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
RepositoryCollectionLinks repositoryCollection() {
|
||||
return new RepositoryCollectionLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class RepositoryCollectionLinks {
|
||||
private final LinkBuilder collectionLinkBuilder;
|
||||
|
||||
RepositoryCollectionLinks(UriInfo uriInfo) {
|
||||
collectionLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryCollectionResource.class);
|
||||
}
|
||||
|
||||
String self() {
|
||||
return collectionLinkBuilder.method("getRepositoryCollectionResource").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
|
||||
String create() {
|
||||
return collectionLinkBuilder.method("getRepositoryCollectionResource").parameters().method("create").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public TagCollectionLinks tagCollection() {
|
||||
return new TagCollectionLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class TagCollectionLinks {
|
||||
private final LinkBuilder tagLinkBuilder;
|
||||
|
||||
TagCollectionLinks(UriInfo uriInfo) {
|
||||
tagLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, TagRootResource.class, TagCollectionResource.class);
|
||||
}
|
||||
|
||||
String self(String namespace, String name) {
|
||||
return tagLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("tags").parameters().method("getTagCollectionResource").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public BranchCollectionLinks branchCollection() {
|
||||
return new BranchCollectionLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class BranchCollectionLinks {
|
||||
private final LinkBuilder branchLinkBuilder;
|
||||
|
||||
BranchCollectionLinks(UriInfo uriInfo) {
|
||||
branchLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, BranchRootResource.class, BranchCollectionResource.class);
|
||||
}
|
||||
|
||||
String self(String namespace, String name) {
|
||||
return branchLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("branches").parameters().method("getBranchCollectionResource").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public ChangesetCollectionLinks changesetCollection() {
|
||||
return new ChangesetCollectionLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class ChangesetCollectionLinks {
|
||||
private final LinkBuilder changesetLinkBuilder;
|
||||
|
||||
ChangesetCollectionLinks(UriInfo uriInfo) {
|
||||
changesetLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ChangesetRootResource.class, ChangesetCollectionResource.class);
|
||||
}
|
||||
|
||||
String self(String namespace, String name) {
|
||||
return changesetLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("changesets").parameters().method("getChangesetCollectionResource").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public SourceCollectionLinks sourceCollection() {
|
||||
return new SourceCollectionLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class SourceCollectionLinks {
|
||||
private final LinkBuilder sourceLinkBuilder;
|
||||
|
||||
SourceCollectionLinks(UriInfo uriInfo) {
|
||||
sourceLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, SourceRootResource.class, SourceCollectionResource.class);
|
||||
}
|
||||
|
||||
String self(String namespace, String name) {
|
||||
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("getSourceCollectionResource").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public PermissionCollectionLinks permissionCollection() {
|
||||
return new PermissionCollectionLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class PermissionCollectionLinks {
|
||||
private final LinkBuilder permissionLinkBuilder;
|
||||
|
||||
PermissionCollectionLinks(UriInfo uriInfo) {
|
||||
permissionLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, PermissionRootResource.class, PermissionCollectionResource.class);
|
||||
}
|
||||
|
||||
String self(String namespace, String name) {
|
||||
return permissionLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("permissions").parameters().method("getPermissionCollectionResource").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import sonia.scm.Manager;
|
||||
import sonia.scm.ModelObject;
|
||||
import sonia.scm.api.rest.resources.AbstractManagerResource;
|
||||
|
||||
import javax.ws.rs.core.GenericEntity;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* Adapter from resource http endpoints to managers, for Single resources (e.g. {@code /user/name}).
|
||||
*
|
||||
* Provides common CRUD operations and DTO to Model Object mapping to keep Resources more DRY.
|
||||
*
|
||||
* @param <MODEL_OBJECT> The type of the model object, eg. {@link sonia.scm.user.User}.
|
||||
* @param <DTO> The corresponding transport object, eg. {@link UserDto}.
|
||||
* @param <EXCEPTION> The exception type for the model object, eg. {@link sonia.scm.user.UserException}.
|
||||
*
|
||||
* @see CollectionResourceManagerAdapter
|
||||
*/
|
||||
@SuppressWarnings("squid:S00119") // "MODEL_OBJECT" is much more meaningful than "M", right?
|
||||
class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
DTO extends HalRepresentation,
|
||||
EXCEPTION extends Exception> extends AbstractManagerResource<MODEL_OBJECT, EXCEPTION> {
|
||||
|
||||
SingleResourceManagerAdapter(Manager<MODEL_OBJECT, EXCEPTION> manager, Class<MODEL_OBJECT> type) {
|
||||
super(manager, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the model object for the given id, transforms it to a dto and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. no matching object for the id or missing privileges.
|
||||
*/
|
||||
Response get(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, DTO> mapToDto) {
|
||||
return reader.get()
|
||||
.map(mapToDto)
|
||||
.map(Response::ok)
|
||||
.map(Response.ResponseBuilder::build)
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the model object for the given id according to the given function and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. no matching object for the id or missing privileges.
|
||||
*/
|
||||
public Response update(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey) {
|
||||
Optional<MODEL_OBJECT> existingModelObject = reader.get();
|
||||
if (!existingModelObject.isPresent()) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject.get());
|
||||
if (!hasSameKey.test(changedModelObject)) {
|
||||
return Response.status(BAD_REQUEST).entity("illegal change of id").build();
|
||||
}
|
||||
return update(getId(existingModelObject.get()), changedModelObject);
|
||||
}
|
||||
|
||||
public Response delete(Supplier<Optional<MODEL_OBJECT>> reader) {
|
||||
return reader.get()
|
||||
.map(MODEL_OBJECT::getId)
|
||||
.map(this::delete)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GenericEntity<Collection<MODEL_OBJECT>> createGenericEntity(Collection<MODEL_OBJECT> modelObjects) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getId(MODEL_OBJECT item) {
|
||||
return item.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPathPart() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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 javax.ws.rs.core.Response;
|
||||
|
||||
public class SourceCollectionResource {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
public class SourceRootResource {
|
||||
|
||||
private final Provider<SourceCollectionResource> sourceCollectionResource;
|
||||
|
||||
@Inject
|
||||
public SourceRootResource(Provider<SourceCollectionResource> sourceCollectionResource) {
|
||||
this.sourceCollectionResource = sourceCollectionResource;
|
||||
}
|
||||
|
||||
@Path("")
|
||||
public SourceCollectionResource getSourceCollectionResource() {
|
||||
return sourceCollectionResource.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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 javax.ws.rs.core.Response;
|
||||
|
||||
public class TagCollectionResource {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
public class TagRootResource {
|
||||
|
||||
private final Provider<TagCollectionResource> tagCollectionResource;
|
||||
|
||||
@Inject
|
||||
public TagRootResource(Provider<TagCollectionResource> tagCollectionResource) {
|
||||
this.tagCollectionResource = tagCollectionResource;
|
||||
}
|
||||
|
||||
@Path("")
|
||||
public TagCollectionResource getTagCollectionResource() {
|
||||
return tagCollectionResource.get();
|
||||
}
|
||||
}
|
||||
@@ -28,14 +28,14 @@ public class UserCollectionResource {
|
||||
private final UserCollectionToDtoMapper userCollectionToDtoMapper;
|
||||
private final ResourceLinks resourceLinks;
|
||||
|
||||
private final ResourceManagerAdapter<User, UserDto, UserException> adapter;
|
||||
private final IdResourceManagerAdapter<User, UserDto, UserException> adapter;
|
||||
|
||||
@Inject
|
||||
public UserCollectionResource(UserManager manager, UserDtoToUserMapper dtoToUserMapper,
|
||||
UserCollectionToDtoMapper userCollectionToDtoMapper, ResourceLinks resourceLinks) {
|
||||
this.dtoToUserMapper = dtoToUserMapper;
|
||||
this.userCollectionToDtoMapper = userCollectionToDtoMapper;
|
||||
this.adapter = new ResourceManagerAdapter<>(manager, User.class);
|
||||
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
||||
this.resourceLinks = resourceLinks;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ public class UserResource {
|
||||
private final UserDtoToUserMapper dtoToUserMapper;
|
||||
private final UserToUserDtoMapper userToDtoMapper;
|
||||
|
||||
private final ResourceManagerAdapter<User, UserDto, UserException> adapter;
|
||||
private final IdResourceManagerAdapter<User, UserDto, UserException> adapter;
|
||||
|
||||
@Inject
|
||||
public UserResource(UserDtoToUserMapper dtoToUserMapper, UserToUserDtoMapper userToDtoMapper, UserManager manager) {
|
||||
this.dtoToUserMapper = dtoToUserMapper;
|
||||
this.userToDtoMapper = userToDtoMapper;
|
||||
this.adapter = new ResourceManagerAdapter<>(manager, User.class);
|
||||
this.adapter = new IdResourceManagerAdapter<>(manager, User.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,7 +111,7 @@ public class DefaultGroupManager extends AbstractGroupManager
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void create(Group group) throws GroupException
|
||||
public Group create(Group group) throws GroupException
|
||||
{
|
||||
String type = group.getType();
|
||||
|
||||
@@ -140,6 +140,7 @@ public class DefaultGroupManager extends AbstractGroupManager
|
||||
fireEvent(HandlerEventType.BEFORE_CREATE, group);
|
||||
groupDAO.add(group);
|
||||
fireEvent(HandlerEventType.CREATE, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -128,7 +128,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void create(Repository repository, boolean initRepository)
|
||||
public Repository create(Repository repository, boolean initRepository)
|
||||
throws RepositoryException {
|
||||
logger.info("create repository {} of type {}", repository.getName(), repository.getType());
|
||||
|
||||
@@ -153,12 +153,13 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
fireEvent(HandlerEventType.BEFORE_CREATE, repository);
|
||||
repositoryDAO.add(repository);
|
||||
fireEvent(HandlerEventType.CREATE, repository);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(Repository repository)
|
||||
public Repository create(Repository repository)
|
||||
throws RepositoryException {
|
||||
create(repository, true);
|
||||
return create(repository, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -137,7 +137,7 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
* @throws UserException
|
||||
*/
|
||||
@Override
|
||||
public void create(User user) throws UserException
|
||||
public User create(User user) throws UserException
|
||||
{
|
||||
String type = user.getType();
|
||||
|
||||
@@ -163,6 +163,7 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
fireEvent(HandlerEventType.BEFORE_CREATE, user);
|
||||
userDAO.add(user);
|
||||
fireEvent(HandlerEventType.CREATE, user);
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
@@ -49,8 +48,7 @@ public class GroupRootResourceTest {
|
||||
|
||||
private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ResourceLinks resourceLinks;
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
|
||||
|
||||
@Mock
|
||||
private GroupManager groupManager;
|
||||
@@ -64,15 +62,13 @@ public class GroupRootResourceTest {
|
||||
@Before
|
||||
public void prepareEnvironment() throws IOException, GroupException {
|
||||
initMocks(this);
|
||||
doNothing().when(groupManager).create(groupCaptor.capture());
|
||||
when(groupManager.create(groupCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
|
||||
doNothing().when(groupManager).modify(groupCaptor.capture());
|
||||
|
||||
Group group = createDummyGroup();
|
||||
when(groupManager.getPage(any(), eq(0), eq(10))).thenReturn(new PageResult<>(singletonList(group), 1));
|
||||
when(groupManager.get("admin")).thenReturn(group);
|
||||
|
||||
ResourceLinksMock.initMock(resourceLinks, URI.create("/"));
|
||||
|
||||
GroupCollectionToDtoMapper groupCollectionToDtoMapper = new GroupCollectionToDtoMapper(groupToDtoMapper, resourceLinks);
|
||||
GroupCollectionResource groupCollectionResource = new GroupCollectionResource(groupManager, dtoToGroupMapper, groupCollectionToDtoMapper, resourceLinks);
|
||||
GroupResource groupResource = new GroupResource(groupManager, groupToDtoMapper, dtoToGroupMapper);
|
||||
|
||||
@@ -7,9 +7,7 @@ import org.apache.shiro.util.ThreadState;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import sonia.scm.group.Group;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -24,8 +22,9 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
public class GroupToGroupDtoMapperTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ResourceLinks resourceLinks;
|
||||
private final URI baseUri = URI.create("http://example.com/base/");
|
||||
@SuppressWarnings("unused") // Is injected
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@InjectMocks
|
||||
private GroupToGroupDtoMapperImpl mapper;
|
||||
@@ -38,12 +37,8 @@ public class GroupToGroupDtoMapperTest {
|
||||
@Before
|
||||
public void init() throws URISyntaxException {
|
||||
initMocks(this);
|
||||
URI baseUri = new URI("http://example.com/base/");
|
||||
expectedBaseUri = baseUri.resolve(GroupRootResource.GROUPS_PATH_V2 + "/");
|
||||
subjectThreadState.bind();
|
||||
|
||||
ResourceLinksMock.initMock(resourceLinks, baseUri);
|
||||
|
||||
ThreadContext.bind(subject);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.inject.binder.AnnotatedBindingBuilder;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class MapperModuleTest {
|
||||
|
||||
@Test
|
||||
public void shouldBindToClassesWithDefaultConstructorOnly() {
|
||||
AnnotatedBindingBuilder binding = mock(AnnotatedBindingBuilder.class);
|
||||
ArgumentCaptor<Class> captor = ArgumentCaptor.forClass(Class.class);
|
||||
when(binding.to(captor.capture())).thenReturn(null);
|
||||
new MapperModule() {
|
||||
@Override
|
||||
protected <T> AnnotatedBindingBuilder<T> bind(Class<T> clazz) {
|
||||
return binding;
|
||||
}
|
||||
}.configure();
|
||||
captor.getAllValues().forEach(this::verifyClassCanBeInstantiated);
|
||||
}
|
||||
|
||||
private <T> T verifyClassCanBeInstantiated(Class<T> c) {
|
||||
try {
|
||||
return c.getConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import com.google.common.io.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.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import sonia.scm.PageResult;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
@SubjectAware(
|
||||
username = "trillian",
|
||||
password = "secret",
|
||||
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
||||
)
|
||||
public class RepositoryRootResourceTest {
|
||||
|
||||
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
|
||||
@Rule
|
||||
public ShiroRule shiro = new ShiroRule();
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
private final URI baseUri = URI.create("/");
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryToRepositoryDtoMapperImpl repositoryToDtoMapper;
|
||||
@InjectMocks
|
||||
private RepositoryDtoToRepositoryMapperImpl dtoToRepositoryMapper;
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
initMocks(this);
|
||||
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null);
|
||||
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
|
||||
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailForNotExistingRepository() throws URISyntaxException {
|
||||
when(repositoryManager.getByNamespace(anyString(), anyString())).thenReturn(empty());
|
||||
mockRepository("space", "repo");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/other");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_NOT_FOUND, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindExistingRepository() throws URISyntaxException {
|
||||
mockRepository("space", "repo");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_OK, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("\"name\":\"repo\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapProperties() throws URISyntaxException {
|
||||
Repository repository = mockRepository("space", "repo");
|
||||
repository.setProperty("testKey", "testValue");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertTrue(response.getContentAsString().contains("\"testKey\":\"testValue\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetAll() throws URISyntaxException {
|
||||
PageResult<Repository> singletonPageResult = createSingletonPageResult(mockRepository("space", "repo"));
|
||||
when(repositoryManager.getPage(any(), eq(0), eq(10))).thenReturn(singletonPageResult);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_OK, response.getStatus());
|
||||
assertTrue(response.getContentAsString().contains("\"name\":\"repo\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleUpdateForNotExistingRepository() throws URISyntaxException, IOException {
|
||||
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
|
||||
byte[] repository = Resources.toByteArray(url);
|
||||
when(repositoryManager.getByNamespace(anyString(), anyString())).thenReturn(empty());
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo")
|
||||
.contentType(VndMediaType.REPOSITORY)
|
||||
.content(repository);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_NOT_FOUND, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleUpdateForExistingRepository() throws Exception {
|
||||
mockRepository("space", "repo");
|
||||
|
||||
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
|
||||
byte[] repository = Resources.toByteArray(url);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo")
|
||||
.contentType(VndMediaType.REPOSITORY)
|
||||
.content(repository);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_NO_CONTENT, response.getStatus());
|
||||
verify(repositoryManager).modify(anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleUpdateForExistingRepositoryForChangedNamespace() throws Exception {
|
||||
mockRepository("wrong", "repo");
|
||||
|
||||
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
|
||||
byte[] repository = Resources.toByteArray(url);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "wrong/repo")
|
||||
.contentType(VndMediaType.REPOSITORY)
|
||||
.content(repository);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_BAD_REQUEST, response.getStatus());
|
||||
verify(repositoryManager, never()).modify(anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleDeleteForExistingRepository() throws Exception {
|
||||
mockRepository("space", "repo");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.delete("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_NO_CONTENT, response.getStatus());
|
||||
verify(repositoryManager).delete(anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNewRepositoryInCorrectNamespace() throws URISyntaxException, IOException, RepositoryException {
|
||||
when(repositoryManager.create(any())).thenAnswer(invocation -> {
|
||||
Repository repository = (Repository) invocation.getArguments()[0];
|
||||
repository.setNamespace("otherspace");
|
||||
return repository;
|
||||
});
|
||||
|
||||
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
|
||||
byte[] repositoryJson = Resources.toByteArray(url);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2)
|
||||
.contentType(VndMediaType.REPOSITORY)
|
||||
.content(repositoryJson);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_CREATED, response.getStatus());
|
||||
assertEquals("/v2/repositories/otherspace/repo", response.getOutputHeaders().get("Location").get(0).toString());
|
||||
verify(repositoryManager).create(any(Repository.class));
|
||||
}
|
||||
|
||||
private PageResult<Repository> createSingletonPageResult(Repository repository) {
|
||||
return new PageResult<>(singletonList(repository), 0);
|
||||
}
|
||||
|
||||
private Repository mockRepository(String namespace, String name) {
|
||||
Repository repository = new Repository();
|
||||
repository.setNamespace(namespace);
|
||||
repository.setName(name);
|
||||
String id = namespace + "-" + name;
|
||||
repository.setId(id);
|
||||
when(repositoryManager.getByNamespace(namespace, name)).thenReturn(of(repository));
|
||||
when(repositoryManager.get(id)).thenReturn(repository);
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import sonia.scm.repository.HealthCheckFailure;
|
||||
import sonia.scm.repository.Permission;
|
||||
import sonia.scm.repository.PermissionType;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
@SubjectAware(
|
||||
username = "trillian",
|
||||
password = "secret",
|
||||
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
||||
)
|
||||
public class RepositoryToRepositoryDtoMapperTest {
|
||||
|
||||
@Rule
|
||||
public final ShiroRule rule = new ShiroRule();
|
||||
|
||||
private final URI baseUri = URI.create("http://example.com/base/");
|
||||
@SuppressWarnings("unused") // Is injected
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryToRepositoryDtoMapperImpl mapper;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapSimpleProperties() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals("testspace", dto.getNamespace());
|
||||
assertEquals("test", dto.getName());
|
||||
assertEquals("description", dto.getDescription());
|
||||
assertEquals("git", dto.getType());
|
||||
assertEquals("none@example.com", dto.getContact());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapPropertiesProperty() {
|
||||
Repository repository = createTestRepository();
|
||||
repository.setProperty("testKey", "testValue");
|
||||
|
||||
RepositoryDto dto = mapper.map(repository);
|
||||
|
||||
assertEquals("testValue", dto.getProperties().get("testKey"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "unpriv")
|
||||
public void shouldCreateLinksForUnprivilegedUser() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test",
|
||||
dto.getLinks().getLinkBy("self").get().getHref());
|
||||
assertFalse(dto.getLinks().getLinkBy("update").isPresent());
|
||||
assertFalse(dto.getLinks().getLinkBy("delete").isPresent());
|
||||
assertFalse(dto.getLinks().getLinkBy("permissions").isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateDeleteLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test",
|
||||
dto.getLinks().getLinkBy("delete").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateUpdateLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test",
|
||||
dto.getLinks().getLinkBy("update").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapHealthCheck() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(1, dto.getHealthCheckFailures().size());
|
||||
assertEquals("summary", dto.getHealthCheckFailures().get(0).getSummary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateTagsLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/tags/",
|
||||
dto.getLinks().getLinkBy("tags").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateBranchesLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/branches/",
|
||||
dto.getLinks().getLinkBy("branches").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateChangesetsLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/changesets/",
|
||||
dto.getLinks().getLinkBy("changesets").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSourcesLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/sources/",
|
||||
dto.getLinks().getLinkBy("sources").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreatePermissionsLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/permissions/",
|
||||
dto.getLinks().getLinkBy("permissions").get().getHref());
|
||||
}
|
||||
|
||||
private Repository createTestRepository() {
|
||||
Repository repository = new Repository();
|
||||
repository.setNamespace("testspace");
|
||||
repository.setName("test");
|
||||
repository.setDescription("description");
|
||||
repository.setType("git");
|
||||
repository.setContact("none@example.com");
|
||||
repository.setId("1");
|
||||
repository.setCreationDate(System.currentTimeMillis());
|
||||
repository.setHealthCheckFailures(singletonList(new HealthCheckFailure("1", "summary", "url", "failure")));
|
||||
repository.setPermissions(singletonList(new Permission("permission", PermissionType.READ)));
|
||||
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,29 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.api.v2.resources.GroupRootResource.GROUPS_PATH_V2;
|
||||
import static sonia.scm.api.v2.resources.UserRootResource.USERS_PATH_V2;
|
||||
|
||||
public class ResourceLinksMock {
|
||||
public static void initMock(ResourceLinks resourceLinks, URI baseUri) {
|
||||
when(resourceLinks.user().self(anyString())).thenAnswer(invocation -> baseUri + USERS_PATH_V2 + invocation.getArguments()[0]);
|
||||
when(resourceLinks.user().update(anyString())).thenAnswer(invocation -> baseUri + USERS_PATH_V2 + invocation.getArguments()[0]);
|
||||
when(resourceLinks.user().delete(anyString())).thenAnswer(invocation -> baseUri + USERS_PATH_V2 + invocation.getArguments()[0]);
|
||||
public static ResourceLinks createMock(URI baseUri) {
|
||||
ResourceLinks resourceLinks = mock(ResourceLinks.class);
|
||||
|
||||
when(resourceLinks.userCollection().self()).thenAnswer(invocation -> baseUri + USERS_PATH_V2);
|
||||
when(resourceLinks.userCollection().create()).thenAnswer(invocation -> baseUri + USERS_PATH_V2);
|
||||
UriInfo uriInfo = mock(UriInfo.class);
|
||||
when(uriInfo.getBaseUri()).thenReturn(baseUri);
|
||||
|
||||
when(resourceLinks.group().self(anyString())).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2 + invocation.getArguments()[0]);
|
||||
when(resourceLinks.group().update(anyString())).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2 + invocation.getArguments()[0]);
|
||||
when(resourceLinks.group().delete(anyString())).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2 + invocation.getArguments()[0]);
|
||||
|
||||
when(resourceLinks.groupCollection().self()).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2);
|
||||
when(resourceLinks.groupCollection().create()).thenAnswer(invocation -> baseUri + GROUPS_PATH_V2);
|
||||
when(resourceLinks.user()).thenReturn(new ResourceLinks.UserLinks(uriInfo));
|
||||
when(resourceLinks.userCollection()).thenReturn(new ResourceLinks.UserCollectionLinks(uriInfo));
|
||||
when(resourceLinks.group()).thenReturn(new ResourceLinks.GroupLinks(uriInfo));
|
||||
when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo));
|
||||
when(resourceLinks.repository()).thenReturn(new ResourceLinks.RepositoryLinks(uriInfo));
|
||||
when(resourceLinks.repositoryCollection()).thenReturn(new ResourceLinks.RepositoryCollectionLinks(uriInfo));
|
||||
when(resourceLinks.tagCollection()).thenReturn(new ResourceLinks.TagCollectionLinks(uriInfo));
|
||||
when(resourceLinks.branchCollection()).thenReturn(new ResourceLinks.BranchCollectionLinks(uriInfo));
|
||||
when(resourceLinks.changesetCollection()).thenReturn(new ResourceLinks.ChangesetCollectionLinks(uriInfo));
|
||||
when(resourceLinks.sourceCollection()).thenReturn(new ResourceLinks.SourceCollectionLinks(uriInfo));
|
||||
when(resourceLinks.permissionCollection()).thenReturn(new ResourceLinks.PermissionCollectionLinks(uriInfo));
|
||||
return resourceLinks;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,54 @@ public class ResourceLinksTest {
|
||||
assertEquals(BASE_URL + GroupRootResource.GROUPS_PATH_V2, url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectRepositorySelfUrl() {
|
||||
String url = resourceLinks.repository().self("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectRepositoryDeleteUrl() {
|
||||
String url = resourceLinks.repository().delete("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectRepositoryUpdateUrl() {
|
||||
String url = resourceLinks.repository().update("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectTagCollectionUrl() {
|
||||
String url = resourceLinks.tagCollection().self("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/tags/", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectBranchCollectionUrl() {
|
||||
String url = resourceLinks.branchCollection().self("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/branches/", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectChangesetCollectionUrl() {
|
||||
String url = resourceLinks.changesetCollection().self("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/changesets/", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectSourceCollectionUrl() {
|
||||
String url = resourceLinks.sourceCollection().self("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources/", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCorrectPermissionCollectionUrl() {
|
||||
String url = resourceLinks.sourceCollection().self("space", "repo");
|
||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources/", url);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initUriInfo() {
|
||||
initMocks(this);
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.apache.shiro.util.ThreadContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import sonia.scm.PageResult;
|
||||
@@ -28,8 +27,8 @@ import static sonia.scm.PageResult.createPage;
|
||||
|
||||
public class UserCollectionToDtoMapperTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ResourceLinks resourceLinks;
|
||||
private final URI baseUri = URI.create("http://example.com/base/");
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
@Mock
|
||||
private UserToUserDtoMapper userToDtoMapper;
|
||||
@Mock
|
||||
@@ -45,9 +44,7 @@ public class UserCollectionToDtoMapperTest {
|
||||
@Before
|
||||
public void init() throws URISyntaxException {
|
||||
initMocks(this);
|
||||
URI baseUri = new URI("http://example.com/base/");
|
||||
expectedBaseUri = baseUri.resolve(UserRootResource.USERS_PATH_V2 + "/");
|
||||
ResourceLinksMock.initMock(resourceLinks, baseUri);
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
@@ -51,8 +50,7 @@ public class UserRootResourceTest {
|
||||
|
||||
private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ResourceLinks resourceLinks;
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
|
||||
|
||||
@Mock
|
||||
private PasswordService passwordService;
|
||||
@@ -66,15 +64,13 @@ public class UserRootResourceTest {
|
||||
private ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() throws IOException, UserException {
|
||||
public void prepareEnvironment() throws UserException {
|
||||
initMocks(this);
|
||||
User dummyUser = createDummyUser("Neo");
|
||||
doNothing().when(userManager).create(userCaptor.capture());
|
||||
when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]);
|
||||
doNothing().when(userManager).modify(userCaptor.capture());
|
||||
doNothing().when(userManager).delete(userCaptor.capture());
|
||||
|
||||
ResourceLinksMock.initMock(resourceLinks, URI.create("/"));
|
||||
|
||||
UserCollectionToDtoMapper userCollectionToDtoMapper = new UserCollectionToDtoMapper(userToDtoMapper, resourceLinks);
|
||||
UserCollectionResource userCollectionResource = new UserCollectionResource(userManager, dtoToUserMapper,
|
||||
userCollectionToDtoMapper, resourceLinks);
|
||||
|
||||
@@ -7,14 +7,11 @@ import org.apache.shiro.util.ThreadState;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import sonia.scm.api.rest.resources.UserResource;
|
||||
import sonia.scm.user.User;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -25,8 +22,9 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
public class UserToUserDtoMapperTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ResourceLinks resourceLinks;
|
||||
private final URI baseUri = URI.create("http://example.com/base/");
|
||||
@SuppressWarnings("unused") // Is injected
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@InjectMocks
|
||||
private UserToUserDtoMapperImpl mapper;
|
||||
@@ -37,12 +35,10 @@ public class UserToUserDtoMapperTest {
|
||||
private URI expectedBaseUri;
|
||||
|
||||
@Before
|
||||
public void init() throws URISyntaxException {
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
URI baseUri = new URI("http://example.com/base/");
|
||||
expectedBaseUri = baseUri.resolve(UserRootResource.USERS_PATH_V2 + "/");
|
||||
subjectThreadState.bind();
|
||||
ResourceLinksMock.initMock(resourceLinks, baseUri);
|
||||
ThreadContext.bind(subject);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"contact": "none@example.com",
|
||||
"description": "Test repository",
|
||||
"namespace": "space",
|
||||
"name": "repo",
|
||||
"archived": false,
|
||||
"type": "git"
|
||||
}
|
||||
Reference in New Issue
Block a user