mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 23:45:44 +01:00
improve import rest api to handle imports of external repositories via url
This commit is contained in:
@@ -35,8 +35,12 @@ package sonia.scm.api.rest.resources;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
|
||||||
import org.codehaus.enunciate.jaxrs.TypeHint;
|
import org.codehaus.enunciate.jaxrs.TypeHint;
|
||||||
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
|
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
|
||||||
|
|
||||||
@@ -46,28 +50,46 @@ import org.slf4j.LoggerFactory;
|
|||||||
import sonia.scm.NotSupportedFeatuerException;
|
import sonia.scm.NotSupportedFeatuerException;
|
||||||
import sonia.scm.Type;
|
import sonia.scm.Type;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryAllreadyExistExeption;
|
||||||
import sonia.scm.repository.RepositoryException;
|
import sonia.scm.repository.RepositoryException;
|
||||||
import sonia.scm.repository.RepositoryHandler;
|
import sonia.scm.repository.RepositoryHandler;
|
||||||
import sonia.scm.repository.RepositoryManager;
|
import sonia.scm.repository.RepositoryManager;
|
||||||
import sonia.scm.util.SecurityUtil;
|
import sonia.scm.repository.RepositoryType;
|
||||||
|
import sonia.scm.repository.api.Command;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.security.Role;
|
||||||
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.*;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.GenericEntity;
|
import javax.ws.rs.core.GenericEntity;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rest resource for importing repositories.
|
* Rest resource for importing repositories.
|
||||||
@@ -91,15 +113,90 @@ public class RepositoryImportResource
|
|||||||
* Constructs a new repository import resource.
|
* Constructs a new repository import resource.
|
||||||
*
|
*
|
||||||
* @param manager repository manager
|
* @param manager repository manager
|
||||||
|
* @param serviceFactory
|
||||||
*/
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
public RepositoryImportResource(RepositoryManager manager)
|
public RepositoryImportResource(RepositoryManager manager,
|
||||||
|
RepositoryServiceFactory serviceFactory)
|
||||||
{
|
{
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a external repository which is accessible via url. The method can
|
||||||
|
* only be used, if the repository type supports the {@link Command#PULL}. The
|
||||||
|
* method will return a location header with the url to the imported
|
||||||
|
* repository.
|
||||||
|
*
|
||||||
|
* Status codes:
|
||||||
|
* <ul>
|
||||||
|
* <li>201 created</li>
|
||||||
|
* <li>400 bad request, the import by url feature is not supported by this
|
||||||
|
* type of repositories or the parameters are not valid.</li>
|
||||||
|
* <li>500 internal server error</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param uriInfo uri info
|
||||||
|
* @param type repository type
|
||||||
|
* @param request request object
|
||||||
|
*
|
||||||
|
* @return empty response with location header which points to the imported
|
||||||
|
* repository
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("{type}/url")
|
||||||
|
@TypeHint(Repository.class)
|
||||||
|
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||||
|
public Response importFromUrl(@Context UriInfo uriInfo,
|
||||||
|
@PathParam("type") String type, UrlImportRequest request)
|
||||||
|
{
|
||||||
|
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||||
|
checkNotNull(request, "request is required");
|
||||||
|
checkArgument(!Strings.isNullOrEmpty(request.getName()),
|
||||||
|
"request does not contain name of the repository");
|
||||||
|
checkArgument(!Strings.isNullOrEmpty(request.getUrl()),
|
||||||
|
"request does not contain url of the remote repository");
|
||||||
|
|
||||||
|
RepositoryHandler handler = manager.getHandler(type);
|
||||||
|
|
||||||
|
if (handler == null)
|
||||||
|
{
|
||||||
|
logger.warn("no handler for type {} found", type);
|
||||||
|
|
||||||
|
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type t = handler.getType();
|
||||||
|
|
||||||
|
checkSupport(t, Command.PULL, request);
|
||||||
|
|
||||||
|
Repository repository = create(type, request.getName());
|
||||||
|
RepositoryService service = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
service = serviceFactory.create(repository);
|
||||||
|
service.getPullCommand().pull(request.getUrl());
|
||||||
|
}
|
||||||
|
catch (RepositoryException ex)
|
||||||
|
{
|
||||||
|
handleImportFailure(ex, repository);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
handleImportFailure(ex, repository);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IOUtil.close(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildResponse(uriInfo, repository);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports repositories of the given type from the configured repository
|
* Imports repositories of the given type from the configured repository
|
||||||
* directory. This method requires admin privileges.<br />
|
* directory. This method requires admin privileges.<br />
|
||||||
@@ -122,7 +219,7 @@ public class RepositoryImportResource
|
|||||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||||
public Response importRepositories(@PathParam("type") String type)
|
public Response importRepositories(@PathParam("type") String type)
|
||||||
{
|
{
|
||||||
SecurityUtil.assertIsAdmin();
|
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||||
|
|
||||||
List<Repository> repositories = new ArrayList<Repository>();
|
List<Repository> repositories = new ArrayList<Repository>();
|
||||||
RepositoryHandler handler = manager.getHandler(type);
|
RepositoryHandler handler = manager.getHandler(type);
|
||||||
@@ -180,7 +277,9 @@ public class RepositoryImportResource
|
|||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of repository types, which support the import feature.
|
* Returns a list of repository types, which support the directory import
|
||||||
|
* feature.
|
||||||
|
*
|
||||||
* This method requires admin privileges.<br />
|
* This method requires admin privileges.<br />
|
||||||
* <br />
|
* <br />
|
||||||
* Status codes:
|
* Status codes:
|
||||||
@@ -188,6 +287,7 @@ public class RepositoryImportResource
|
|||||||
* <li>200 ok, successful</li>
|
* <li>200 ok, successful</li>
|
||||||
* <li>400 bad request, the import feature is not
|
* <li>400 bad request, the import feature is not
|
||||||
* supported by this type of repositories.</li>
|
* supported by this type of repositories.</li>
|
||||||
|
* <li>409 conflict, a repository with the name already exists.</li>
|
||||||
* <li>500 internal server error</li>
|
* <li>500 internal server error</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@@ -198,7 +298,7 @@ public class RepositoryImportResource
|
|||||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||||
public Response getImportableTypes()
|
public Response getImportableTypes()
|
||||||
{
|
{
|
||||||
SecurityUtil.assertIsAdmin();
|
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||||
|
|
||||||
List<Type> types = new ArrayList<Type>();
|
List<Type> types = new ArrayList<Type>();
|
||||||
Collection<Type> handlerTypes = manager.getTypes();
|
Collection<Type> handlerTypes = manager.getTypes();
|
||||||
@@ -242,8 +342,218 @@ public class RepositoryImportResource
|
|||||||
//J+
|
//J+
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build rest response for repository.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param uriInfo uri info
|
||||||
|
* @param repository imported repository
|
||||||
|
*
|
||||||
|
* @return rest response
|
||||||
|
*/
|
||||||
|
private Response buildResponse(UriInfo uriInfo, Repository repository)
|
||||||
|
{
|
||||||
|
URI location = uriInfo.getBaseUriBuilder().path(
|
||||||
|
RepositoryResource.class).path(repository.getId()).build();
|
||||||
|
|
||||||
|
return Response.created(location).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check repository type for support for the given command.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param type repository type
|
||||||
|
* @param cmd command
|
||||||
|
* @param request request object
|
||||||
|
*/
|
||||||
|
private void checkSupport(Type type, Command cmd, Object request)
|
||||||
|
{
|
||||||
|
if (!(type instanceof RepositoryType))
|
||||||
|
{
|
||||||
|
logger.warn("type {} is not a repository type", type.getName());
|
||||||
|
|
||||||
|
throw new WebApplicationException(Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Command> cmds = ((RepositoryType) type).getSupportedCommands();
|
||||||
|
|
||||||
|
if (!cmds.contains(cmd))
|
||||||
|
{
|
||||||
|
logger.warn("type {} does not support this type of import: {}",
|
||||||
|
type.getName(), request);
|
||||||
|
|
||||||
|
throw new WebApplicationException(Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new repository with the given name and type.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param type repository type
|
||||||
|
* @param name repository name
|
||||||
|
*
|
||||||
|
* @return newly created repository
|
||||||
|
*/
|
||||||
|
private Repository create(String type, String name)
|
||||||
|
{
|
||||||
|
Repository repository = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
repository = new Repository(null, type, name);
|
||||||
|
manager.create(repository);
|
||||||
|
}
|
||||||
|
catch (RepositoryAllreadyExistExeption ex)
|
||||||
|
{
|
||||||
|
logger.warn("a {} repository with the name {} already exists", ex);
|
||||||
|
|
||||||
|
throw new WebApplicationException(Response.Status.CONFLICT);
|
||||||
|
}
|
||||||
|
catch (RepositoryException ex)
|
||||||
|
{
|
||||||
|
handleGenericCreationFailure(ex, type, name);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
handleGenericCreationFailure(ex, type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle creation failures.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param ex exception
|
||||||
|
* @param type repository type
|
||||||
|
* @param name name of the repository
|
||||||
|
*/
|
||||||
|
private void handleGenericCreationFailure(Exception ex, String type,
|
||||||
|
String name)
|
||||||
|
{
|
||||||
|
logger.error(String.format("could not create repository {} with type {}",
|
||||||
|
type, name), ex);
|
||||||
|
|
||||||
|
throw new WebApplicationException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle import failures.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param ex exception
|
||||||
|
* @param repository repository
|
||||||
|
*/
|
||||||
|
private void handleImportFailure(Exception ex, Repository repository)
|
||||||
|
{
|
||||||
|
logger.error("import for repository failed, delete repository", ex);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
manager.delete(repository);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.error("can not delete repository", e);
|
||||||
|
}
|
||||||
|
catch (RepositoryException e)
|
||||||
|
{
|
||||||
|
logger.error("can not delete repository", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new WebApplicationException(ex,
|
||||||
|
Response.Status.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- inner classes --------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for importing external repositories which are accessible via url.
|
||||||
|
*/
|
||||||
|
@XmlRootElement(name = "import")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public static class UrlImportRequest
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs ...
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public UrlImportRequest() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@link UrlImportRequest}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param name name of the repository
|
||||||
|
* @param url external url of the repository
|
||||||
|
*/
|
||||||
|
public UrlImportRequest(String name, String url)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- methods ------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
//J-
|
||||||
|
return Objects.toStringHelper(this)
|
||||||
|
.add("name", name)
|
||||||
|
.add("url", url)
|
||||||
|
.toString();
|
||||||
|
//J+
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- get methods --------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns name of the repository.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return name of the repository
|
||||||
|
*/
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns external url of the repository.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return external url of the repository
|
||||||
|
*/
|
||||||
|
public String getUrl()
|
||||||
|
{
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- fields -------------------------------------------------------------
|
||||||
|
|
||||||
|
/** name of the repository */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** external url of the repository */
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** repository manager */
|
/** repository manager */
|
||||||
private final RepositoryManager manager;
|
private final RepositoryManager manager;
|
||||||
|
|
||||||
|
/** repository service factory */
|
||||||
|
private final RepositoryServiceFactory serviceFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user