mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-18 03:01:05 +01:00
Merged in feature/redirect_migrated_repositories (pull request #277)
Feature redirect migrated repositories
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
package sonia.scm.migration;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface MigrationDAO {
|
||||||
|
Collection<MigrationInfo> getAll();
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package sonia.scm.migration;
|
||||||
|
|
||||||
|
public class MigrationInfo {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String protocol;
|
||||||
|
private final String originalRepositoryName;
|
||||||
|
private final String namespace;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public MigrationInfo(String id, String protocol, String originalRepositoryName, String namespace, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.protocol = protocol;
|
||||||
|
this.originalRepositoryName = originalRepositoryName;
|
||||||
|
this.namespace = namespace;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtocol() {
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalRepositoryName() {
|
||||||
|
return originalRepositoryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package sonia.scm.legacy;
|
||||||
|
|
||||||
|
import com.google.inject.servlet.ServletModule;
|
||||||
|
import sonia.scm.plugin.Extension;
|
||||||
|
|
||||||
|
@Extension
|
||||||
|
public class LegacyModule extends ServletModule {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureServlets() {
|
||||||
|
filter("/*").through(RepositoryLegacyProtocolRedirectFilter.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package sonia.scm.legacy;
|
||||||
|
|
||||||
|
import sonia.scm.Priority;
|
||||||
|
import sonia.scm.filter.Filters;
|
||||||
|
import sonia.scm.migration.MigrationDAO;
|
||||||
|
import sonia.scm.migration.MigrationInfo;
|
||||||
|
import sonia.scm.repository.RepositoryDAO;
|
||||||
|
import sonia.scm.web.filter.HttpFilter;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Optional.empty;
|
||||||
|
import static java.util.Optional.of;
|
||||||
|
import static org.apache.commons.lang.StringUtils.isEmpty;
|
||||||
|
|
||||||
|
@Priority(Filters.PRIORITY_BASEURL)
|
||||||
|
@Singleton
|
||||||
|
public class RepositoryLegacyProtocolRedirectFilter extends HttpFilter {
|
||||||
|
|
||||||
|
private final ProtocolBasedLegacyRepositoryInfo info;
|
||||||
|
private final RepositoryDAO repositoryDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RepositoryLegacyProtocolRedirectFilter(MigrationDAO migrationDAO, RepositoryDAO repositoryDao) {
|
||||||
|
this.info = load(migrationDAO);
|
||||||
|
this.repositoryDao = repositoryDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProtocolBasedLegacyRepositoryInfo load(MigrationDAO migrationDAO) {
|
||||||
|
return new ProtocolBasedLegacyRepositoryInfo(migrationDAO.getAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||||
|
new Worker(request, response, chain).doFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Worker {
|
||||||
|
private final HttpServletRequest request;
|
||||||
|
private final HttpServletResponse response;
|
||||||
|
private final FilterChain chain;
|
||||||
|
|
||||||
|
private Worker(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
|
||||||
|
this.request = request;
|
||||||
|
this.response = response;
|
||||||
|
this.chain = chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doFilter() throws IOException, ServletException {
|
||||||
|
String servletPath = getServletPathWithoutLeadingSlash();
|
||||||
|
String[] pathElements = servletPath.split("/");
|
||||||
|
if (pathElements.length > 0) {
|
||||||
|
checkPathElements(servletPath, pathElements);
|
||||||
|
} else {
|
||||||
|
noRedirect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPathElements(String servletPath, String[] pathElements) throws IOException, ServletException {
|
||||||
|
Optional<MigrationInfo> migrationInfo = info.findRepository(asList(pathElements));
|
||||||
|
if (migrationInfo.isPresent()) {
|
||||||
|
doRedirect(servletPath, migrationInfo.get());
|
||||||
|
} else {
|
||||||
|
noRedirect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRedirect(String servletPath, MigrationInfo migrationInfo) throws IOException, ServletException {
|
||||||
|
if (repositoryDao.get(migrationInfo.getId()) == null) {
|
||||||
|
noRedirect();
|
||||||
|
} else {
|
||||||
|
String furtherPath = servletPath.substring(migrationInfo.getProtocol().length() + 1 + migrationInfo.getOriginalRepositoryName().length());
|
||||||
|
String queryString = request.getQueryString();
|
||||||
|
if (isEmpty(queryString)) {
|
||||||
|
redirectWithoutQueryParameters(migrationInfo, furtherPath);
|
||||||
|
} else {
|
||||||
|
redirectWithQueryParameters(migrationInfo, furtherPath, queryString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void redirectWithoutQueryParameters(MigrationInfo migrationInfo, String furtherPath) throws IOException {
|
||||||
|
response.sendRedirect(String.format("%s/repo/%s/%s%s", request.getContextPath(), migrationInfo.getNamespace(), migrationInfo.getName(), furtherPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void redirectWithQueryParameters(MigrationInfo migrationInfo, String furtherPath, String queryString) throws IOException {
|
||||||
|
response.sendRedirect(String.format("%s/repo/%s/%s%s?%s", request.getContextPath(), migrationInfo.getNamespace(), migrationInfo.getName(), furtherPath, queryString));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noRedirect() throws IOException, ServletException {
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getServletPathWithoutLeadingSlash() {
|
||||||
|
String servletPath = request.getServletPath();
|
||||||
|
if (servletPath.startsWith("/")) {
|
||||||
|
return servletPath.substring(1);
|
||||||
|
} else {
|
||||||
|
return servletPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ProtocolBasedLegacyRepositoryInfo {
|
||||||
|
|
||||||
|
private final Map<String, LegacyRepositoryInfoCollection> infosForProtocol = new HashMap<>();
|
||||||
|
|
||||||
|
ProtocolBasedLegacyRepositoryInfo(Collection<MigrationInfo> all) {
|
||||||
|
all.forEach(this::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(MigrationInfo migrationInfo) {
|
||||||
|
String protocol = migrationInfo.getProtocol();
|
||||||
|
LegacyRepositoryInfoCollection legacyRepositoryInfoCollection = infosForProtocol.computeIfAbsent(protocol, x -> new LegacyRepositoryInfoCollection());
|
||||||
|
legacyRepositoryInfoCollection.add(migrationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<MigrationInfo> findRepository(List<String> pathElements) {
|
||||||
|
String protocol = pathElements.get(0);
|
||||||
|
if (!isProtocol(protocol)) {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
return infosForProtocol.get(protocol).findRepository(removeFirstElement(pathElements));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isProtocol(String protocol) {
|
||||||
|
return infosForProtocol.containsKey(protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LegacyRepositoryInfoCollection {
|
||||||
|
|
||||||
|
private final Map<String, MigrationInfo> repositories = new HashMap<>();
|
||||||
|
private final Map<String, LegacyRepositoryInfoCollection> next = new HashMap<>();
|
||||||
|
|
||||||
|
Optional<MigrationInfo> findRepository(List<String> pathElements) {
|
||||||
|
String firstPathElement = pathElements.get(0);
|
||||||
|
if (repositories.containsKey(firstPathElement)) {
|
||||||
|
return of(repositories.get(firstPathElement));
|
||||||
|
} else if (next.containsKey(firstPathElement)) {
|
||||||
|
return next.get(firstPathElement).findRepository(removeFirstElement(pathElements));
|
||||||
|
} else {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(MigrationInfo migrationInfo) {
|
||||||
|
String originalRepositoryName = migrationInfo.getOriginalRepositoryName();
|
||||||
|
List<String> originalRepositoryNameParts = asList(originalRepositoryName.split("/"));
|
||||||
|
add(migrationInfo, originalRepositoryNameParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(MigrationInfo migrationInfo, List<String> originalRepositoryNameParts) {
|
||||||
|
if (originalRepositoryNameParts.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("cannot handle empty name");
|
||||||
|
} else if (originalRepositoryNameParts.size() == 1) {
|
||||||
|
repositories.put(originalRepositoryNameParts.get(0), migrationInfo);
|
||||||
|
} else {
|
||||||
|
LegacyRepositoryInfoCollection subCollection = next.computeIfAbsent(originalRepositoryNameParts.get(0), x -> new LegacyRepositoryInfoCollection());
|
||||||
|
subCollection.add(migrationInfo, removeFirstElement(originalRepositoryNameParts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> removeFirstElement(List<T> originalRepositoryNameParts) {
|
||||||
|
return originalRepositoryNameParts.subList(1, originalRepositoryNameParts.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package sonia.scm.legacy;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.migration.MigrationDAO;
|
||||||
|
import sonia.scm.migration.MigrationInfo;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryDAO;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class RepositoryLegacyProtocolRedirectFilterTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
MigrationDAO migrationDAO;
|
||||||
|
@Mock
|
||||||
|
RepositoryDAO repositoryDao;
|
||||||
|
@Mock
|
||||||
|
HttpServletRequest request;
|
||||||
|
@Mock
|
||||||
|
HttpServletResponse response;
|
||||||
|
@Mock
|
||||||
|
FilterChain filterChain;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void initRequest() {
|
||||||
|
lenient().when(request.getContextPath()).thenReturn("/scm");
|
||||||
|
lenient().when(request.getQueryString()).thenReturn("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRedirectForEmptyMigrationList() throws IOException, ServletException {
|
||||||
|
when(request.getServletPath()).thenReturn("/git/old/name");
|
||||||
|
|
||||||
|
new RepositoryLegacyProtocolRedirectFilter(migrationDAO, repositoryDao).doFilter(request, response, filterChain);
|
||||||
|
|
||||||
|
verify(filterChain).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRedirectForExistingRepository() throws IOException, ServletException {
|
||||||
|
when(migrationDAO.getAll()).thenReturn(singletonList(new MigrationInfo("id", "git", "old/name", "namespace", "name")));
|
||||||
|
when(repositoryDao.get("id")).thenReturn(new Repository());
|
||||||
|
when(request.getServletPath()).thenReturn("/git/old/name");
|
||||||
|
|
||||||
|
new RepositoryLegacyProtocolRedirectFilter(migrationDAO, repositoryDao).doFilter(request, response, filterChain);
|
||||||
|
|
||||||
|
verify(response).sendRedirect("/scm/repo/namespace/name");
|
||||||
|
verify(filterChain, never()).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRedirectForExistingRepositoryWithFurtherPathElements() throws IOException, ServletException {
|
||||||
|
when(migrationDAO.getAll()).thenReturn(singletonList(new MigrationInfo("id", "git", "old/name", "namespace", "name")));
|
||||||
|
when(repositoryDao.get("id")).thenReturn(new Repository());
|
||||||
|
when(request.getServletPath()).thenReturn("/git/old/name/info/refs");
|
||||||
|
|
||||||
|
new RepositoryLegacyProtocolRedirectFilter(migrationDAO, repositoryDao).doFilter(request, response, filterChain);
|
||||||
|
|
||||||
|
verify(response).sendRedirect("/scm/repo/namespace/name/info/refs");
|
||||||
|
verify(filterChain, never()).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRedirectWithQueryParameters() throws IOException, ServletException {
|
||||||
|
when(migrationDAO.getAll()).thenReturn(singletonList(new MigrationInfo("id", "git", "old/name", "namespace", "name")));
|
||||||
|
when(repositoryDao.get("id")).thenReturn(new Repository());
|
||||||
|
when(request.getServletPath()).thenReturn("/git/old/name/info/refs");
|
||||||
|
when(request.getQueryString()).thenReturn("parameter=value");
|
||||||
|
|
||||||
|
new RepositoryLegacyProtocolRedirectFilter(migrationDAO, repositoryDao).doFilter(request, response, filterChain);
|
||||||
|
|
||||||
|
verify(response).sendRedirect("/scm/repo/namespace/name/info/refs?parameter=value");
|
||||||
|
verify(filterChain, never()).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRedirectWhenRepositoryHasBeenDeleted() throws IOException, ServletException {
|
||||||
|
when(migrationDAO.getAll()).thenReturn(singletonList(new MigrationInfo("id", "git", "old/name", "namespace", "name")));
|
||||||
|
when(repositoryDao.get("id")).thenReturn(null);
|
||||||
|
when(request.getServletPath()).thenReturn("/git/old/name");
|
||||||
|
|
||||||
|
new RepositoryLegacyProtocolRedirectFilter(migrationDAO, repositoryDao).doFilter(request, response, filterChain);
|
||||||
|
|
||||||
|
verify(response, never()).sendRedirect(any());
|
||||||
|
verify(filterChain).doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,6 +57,7 @@ import sonia.scm.group.GroupDisplayManager;
|
|||||||
import sonia.scm.group.GroupManager;
|
import sonia.scm.group.GroupManager;
|
||||||
import sonia.scm.group.GroupManagerProvider;
|
import sonia.scm.group.GroupManagerProvider;
|
||||||
import sonia.scm.group.xml.XmlGroupDAO;
|
import sonia.scm.group.xml.XmlGroupDAO;
|
||||||
|
import sonia.scm.migration.MigrationDAO;
|
||||||
import sonia.scm.net.SSLContextProvider;
|
import sonia.scm.net.SSLContextProvider;
|
||||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||||
import sonia.scm.net.ahc.ContentTransformer;
|
import sonia.scm.net.ahc.ContentTransformer;
|
||||||
@@ -97,6 +98,7 @@ import sonia.scm.template.MustacheTemplateEngine;
|
|||||||
import sonia.scm.template.TemplateEngine;
|
import sonia.scm.template.TemplateEngine;
|
||||||
import sonia.scm.template.TemplateEngineFactory;
|
import sonia.scm.template.TemplateEngineFactory;
|
||||||
import sonia.scm.template.TemplateServlet;
|
import sonia.scm.template.TemplateServlet;
|
||||||
|
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
|
||||||
import sonia.scm.user.DefaultUserDisplayManager;
|
import sonia.scm.user.DefaultUserDisplayManager;
|
||||||
import sonia.scm.user.DefaultUserManager;
|
import sonia.scm.user.DefaultUserManager;
|
||||||
import sonia.scm.user.UserDAO;
|
import sonia.scm.user.UserDAO;
|
||||||
@@ -183,6 +185,7 @@ class ScmServletModule extends ServletModule {
|
|||||||
bind(RepositoryDAO.class, XmlRepositoryDAO.class);
|
bind(RepositoryDAO.class, XmlRepositoryDAO.class);
|
||||||
bind(RepositoryRoleDAO.class, XmlRepositoryRoleDAO.class);
|
bind(RepositoryRoleDAO.class, XmlRepositoryRoleDAO.class);
|
||||||
bind(RepositoryRoleManager.class).to(DefaultRepositoryRoleManager.class);
|
bind(RepositoryRoleManager.class).to(DefaultRepositoryRoleManager.class);
|
||||||
|
bind(MigrationDAO.class).to(DefaultMigrationStrategyDAO.class);
|
||||||
|
|
||||||
bindDecorated(RepositoryManager.class, DefaultRepositoryManager.class,
|
bindDecorated(RepositoryManager.class, DefaultRepositoryManager.class,
|
||||||
RepositoryManagerProvider.class);
|
RepositoryManagerProvider.class);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import sonia.scm.lifecycle.RestartEvent;
|
import sonia.scm.lifecycle.RestartEvent;
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.event.ScmEventBus;
|
||||||
import sonia.scm.update.repository.MigrationStrategy;
|
import sonia.scm.update.repository.MigrationStrategy;
|
||||||
import sonia.scm.update.repository.MigrationStrategyDao;
|
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
|
||||||
import sonia.scm.update.repository.V1Repository;
|
import sonia.scm.update.repository.V1Repository;
|
||||||
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
||||||
import sonia.scm.util.ValidationUtil;
|
import sonia.scm.util.ValidationUtil;
|
||||||
@@ -37,10 +37,10 @@ class MigrationWizardServlet extends HttpServlet {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(MigrationWizardServlet.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MigrationWizardServlet.class);
|
||||||
|
|
||||||
private final XmlRepositoryV1UpdateStep repositoryV1UpdateStep;
|
private final XmlRepositoryV1UpdateStep repositoryV1UpdateStep;
|
||||||
private final MigrationStrategyDao migrationStrategyDao;
|
private final DefaultMigrationStrategyDAO migrationStrategyDao;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MigrationWizardServlet(XmlRepositoryV1UpdateStep repositoryV1UpdateStep, MigrationStrategyDao migrationStrategyDao) {
|
MigrationWizardServlet(XmlRepositoryV1UpdateStep repositoryV1UpdateStep, DefaultMigrationStrategyDAO migrationStrategyDao) {
|
||||||
this.repositoryV1UpdateStep = repositoryV1UpdateStep;
|
this.repositoryV1UpdateStep = repositoryV1UpdateStep;
|
||||||
this.migrationStrategyDao = migrationStrategyDao;
|
this.migrationStrategyDao = migrationStrategyDao;
|
||||||
}
|
}
|
||||||
@@ -103,11 +103,12 @@ class MigrationWizardServlet extends HttpServlet {
|
|||||||
.forEach(
|
.forEach(
|
||||||
entry-> {
|
entry-> {
|
||||||
String id = entry.getId();
|
String id = entry.getId();
|
||||||
|
String protocol = entry.getType();
|
||||||
String originalName = entry.getOriginalName();
|
String originalName = entry.getOriginalName();
|
||||||
String strategy = req.getParameter("strategy-" + id);
|
String strategy = req.getParameter("strategy-" + id);
|
||||||
String namespace = req.getParameter("namespace-" + id);
|
String namespace = req.getParameter("namespace-" + id);
|
||||||
String name = req.getParameter("name-" + id);
|
String name = req.getParameter("name-" + id);
|
||||||
migrationStrategyDao.set(id, originalName, MigrationStrategy.valueOf(strategy), namespace, name);
|
migrationStrategyDao.set(id, protocol, originalName, MigrationStrategy.valueOf(strategy), namespace, name);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package sonia.scm.update.repository;
|
||||||
|
|
||||||
|
import sonia.scm.migration.MigrationDAO;
|
||||||
|
import sonia.scm.migration.MigrationInfo;
|
||||||
|
import sonia.scm.store.ConfigurationStore;
|
||||||
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class DefaultMigrationStrategyDAO implements MigrationDAO {
|
||||||
|
|
||||||
|
private final RepositoryMigrationPlan plan;
|
||||||
|
private final ConfigurationStore<RepositoryMigrationPlan> store;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultMigrationStrategyDAO(ConfigurationStoreFactory storeFactory) {
|
||||||
|
store = storeFactory.withType(RepositoryMigrationPlan.class).withName("migration-plan").build();
|
||||||
|
this.plan = store.getOptional().orElse(new RepositoryMigrationPlan());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> get(String id) {
|
||||||
|
return plan.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String repositoryId, String protocol, String originalName, MigrationStrategy strategy, String newNamespace, String newName) {
|
||||||
|
plan.set(repositoryId, protocol, originalName, strategy, newNamespace, newName);
|
||||||
|
store.set(plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<MigrationInfo> getAll() {
|
||||||
|
return plan
|
||||||
|
.getEntries()
|
||||||
|
.stream()
|
||||||
|
.map(e -> new MigrationInfo(e.getRepositoryId(), e.getProtocol(), e.getOriginalName(), e.getNewNamespace(), e.getNewName()))
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package sonia.scm.update.repository;
|
|
||||||
|
|
||||||
import sonia.scm.store.ConfigurationStore;
|
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class MigrationStrategyDao {
|
|
||||||
|
|
||||||
private final RepositoryMigrationPlan plan;
|
|
||||||
private final ConfigurationStore<RepositoryMigrationPlan> store;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public MigrationStrategyDao(ConfigurationStoreFactory storeFactory) {
|
|
||||||
store = storeFactory.withType(RepositoryMigrationPlan.class).withName("migration-plan").build();
|
|
||||||
this.plan = store.getOptional().orElse(new RepositoryMigrationPlan());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> get(String id) {
|
|
||||||
return plan.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(String repositoryId, String originalName, MigrationStrategy strategy, String newNamespace, String newName) {
|
|
||||||
plan.set(repositoryId, originalName, strategy, newNamespace, newName);
|
|
||||||
store.set(plan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,8 @@ import javax.xml.bind.annotation.XmlAccessType;
|
|||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -29,14 +31,18 @@ class RepositoryMigrationPlan {
|
|||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(String repositoryId, String originalName, MigrationStrategy strategy, String newNamespace, String newName) {
|
public Collection<RepositoryMigrationEntry> getEntries() {
|
||||||
|
return Collections.unmodifiableList(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String repositoryId, String protocol, String originalName, MigrationStrategy strategy, String newNamespace, String newName) {
|
||||||
Optional<RepositoryMigrationEntry> entry = get(repositoryId);
|
Optional<RepositoryMigrationEntry> entry = get(repositoryId);
|
||||||
if (entry.isPresent()) {
|
if (entry.isPresent()) {
|
||||||
entry.get().setStrategy(strategy);
|
entry.get().setStrategy(strategy);
|
||||||
entry.get().setNewNamespace(newNamespace);
|
entry.get().setNewNamespace(newNamespace);
|
||||||
entry.get().setNewName(newName);
|
entry.get().setNewName(newName);
|
||||||
} else {
|
} else {
|
||||||
entries.add(new RepositoryMigrationEntry(repositoryId, originalName, strategy, newNamespace, newName));
|
entries.add(new RepositoryMigrationEntry(repositoryId, protocol, originalName, strategy, newNamespace, newName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +51,7 @@ class RepositoryMigrationPlan {
|
|||||||
static class RepositoryMigrationEntry {
|
static class RepositoryMigrationEntry {
|
||||||
|
|
||||||
private String repositoryId;
|
private String repositoryId;
|
||||||
|
private String protocol;
|
||||||
private String originalName;
|
private String originalName;
|
||||||
private MigrationStrategy dataMigrationStrategy;
|
private MigrationStrategy dataMigrationStrategy;
|
||||||
private String newNamespace;
|
private String newNamespace;
|
||||||
@@ -53,14 +60,23 @@ class RepositoryMigrationPlan {
|
|||||||
RepositoryMigrationEntry() {
|
RepositoryMigrationEntry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
RepositoryMigrationEntry(String repositoryId, String originalName, MigrationStrategy dataMigrationStrategy, String newNamespace, String newName) {
|
RepositoryMigrationEntry(String repositoryId, String protocol, String originalName, MigrationStrategy dataMigrationStrategy, String newNamespace, String newName) {
|
||||||
this.repositoryId = repositoryId;
|
this.repositoryId = repositoryId;
|
||||||
|
this.protocol = protocol;
|
||||||
this.originalName = originalName;
|
this.originalName = originalName;
|
||||||
this.dataMigrationStrategy = dataMigrationStrategy;
|
this.dataMigrationStrategy = dataMigrationStrategy;
|
||||||
this.newNamespace = newNamespace;
|
this.newNamespace = newNamespace;
|
||||||
this.newName = newName;
|
this.newName = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRepositoryId() {
|
||||||
|
return repositoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtocol() {
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
public String getOriginalName() {
|
public String getOriginalName() {
|
||||||
return originalName;
|
return originalName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import static sonia.scm.version.Version.parse;
|
|||||||
* <li>a new entry in the new <code>repository-paths.xml</code> database is written,</li>
|
* <li>a new entry in the new <code>repository-paths.xml</code> database is written,</li>
|
||||||
* <li>the data directory is moved or copied to a SCM v2 consistent directory. How this is done
|
* <li>the data directory is moved or copied to a SCM v2 consistent directory. How this is done
|
||||||
* can be specified by a strategy (@see {@link MigrationStrategy}), that has to be set in
|
* can be specified by a strategy (@see {@link MigrationStrategy}), that has to be set in
|
||||||
* a database file named <code>migration-plan.xml</code></li> (to create this file, use {@link MigrationStrategyDao}),
|
* a database file named <code>migration-plan.xml</code></li> (to create this file, use {@link DefaultMigrationStrategyDAO}),
|
||||||
* and
|
* and
|
||||||
* <li>the new <code>metadata.xml</code> file is created.</li>
|
* <li>the new <code>metadata.xml</code> file is created.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
@@ -63,7 +63,7 @@ public class XmlRepositoryV1UpdateStep implements CoreUpdateStep {
|
|||||||
|
|
||||||
private final SCMContextProvider contextProvider;
|
private final SCMContextProvider contextProvider;
|
||||||
private final XmlRepositoryDAO repositoryDao;
|
private final XmlRepositoryDAO repositoryDao;
|
||||||
private final MigrationStrategyDao migrationStrategyDao;
|
private final DefaultMigrationStrategyDAO migrationStrategyDao;
|
||||||
private final Injector injector;
|
private final Injector injector;
|
||||||
private final ConfigurationEntryStore<V1Properties> propertyStore;
|
private final ConfigurationEntryStore<V1Properties> propertyStore;
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ public class XmlRepositoryV1UpdateStep implements CoreUpdateStep {
|
|||||||
public XmlRepositoryV1UpdateStep(
|
public XmlRepositoryV1UpdateStep(
|
||||||
SCMContextProvider contextProvider,
|
SCMContextProvider contextProvider,
|
||||||
XmlRepositoryDAO repositoryDao,
|
XmlRepositoryDAO repositoryDao,
|
||||||
MigrationStrategyDao migrationStrategyDao,
|
DefaultMigrationStrategyDAO migrationStrategyDao,
|
||||||
Injector injector,
|
Injector injector,
|
||||||
ConfigurationEntryStoreFactory configurationEntryStoreFactory
|
ConfigurationEntryStoreFactory configurationEntryStoreFactory
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import sonia.scm.update.repository.MigrationStrategy;
|
import sonia.scm.update.repository.MigrationStrategy;
|
||||||
import sonia.scm.update.repository.MigrationStrategyDao;
|
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
|
||||||
import sonia.scm.update.repository.V1Repository;
|
import sonia.scm.update.repository.V1Repository;
|
||||||
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class MigrationWizardServletTest {
|
|||||||
@Mock
|
@Mock
|
||||||
XmlRepositoryV1UpdateStep updateStep;
|
XmlRepositoryV1UpdateStep updateStep;
|
||||||
@Mock
|
@Mock
|
||||||
MigrationStrategyDao migrationStrategyDao;
|
DefaultMigrationStrategyDAO migrationStrategyDao;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
HttpServletRequest request;
|
HttpServletRequest request;
|
||||||
@@ -233,6 +233,6 @@ class MigrationWizardServletTest {
|
|||||||
|
|
||||||
servlet.doPost(request, response);
|
servlet.doPost(request, response);
|
||||||
|
|
||||||
verify(migrationStrategyDao).set("id", "name", MigrationStrategy.COPY, "namespace", "name");
|
verify(migrationStrategyDao).set("id", "git", "name", MigrationStrategy.COPY, "namespace", "name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import sonia.scm.SCMContextProvider;
|
|||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ import static sonia.scm.update.repository.MigrationStrategy.INLINE;
|
|||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@ExtendWith(TempDirectory.class)
|
@ExtendWith(TempDirectory.class)
|
||||||
class MigrationStrategyDaoTest {
|
class DefaultMigrationStrategyDAOTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
SCMContextProvider contextProvider;
|
SCMContextProvider contextProvider;
|
||||||
@@ -36,7 +35,7 @@ class MigrationStrategyDaoTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnEmptyOptionalWhenStoreIsEmpty() {
|
void shouldReturnEmptyOptionalWhenStoreIsEmpty() {
|
||||||
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
|
DefaultMigrationStrategyDAO dao = new DefaultMigrationStrategyDAO(storeFactory);
|
||||||
|
|
||||||
Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("any");
|
Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("any");
|
||||||
|
|
||||||
@@ -45,9 +44,9 @@ class MigrationStrategyDaoTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnNewValue() {
|
void shouldReturnNewValue() {
|
||||||
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
|
DefaultMigrationStrategyDAO dao = new DefaultMigrationStrategyDAO(storeFactory);
|
||||||
|
|
||||||
dao.set("id", "originalName", INLINE, "space", "name");
|
dao.set("id", "git", "originalName", INLINE, "space", "name");
|
||||||
|
|
||||||
Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("id");
|
Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("id");
|
||||||
|
|
||||||
@@ -66,14 +65,14 @@ class MigrationStrategyDaoTest {
|
|||||||
class WithExistingDatabase {
|
class WithExistingDatabase {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void initExistingDatabase() {
|
void initExistingDatabase() {
|
||||||
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
|
DefaultMigrationStrategyDAO dao = new DefaultMigrationStrategyDAO(storeFactory);
|
||||||
|
|
||||||
dao.set("id", "originalName", INLINE, "space", "name");
|
dao.set("id", "git", "originalName", INLINE, "space", "name");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindExistingValue() {
|
void shouldFindExistingValue() {
|
||||||
MigrationStrategyDao dao = new MigrationStrategyDao(storeFactory);
|
DefaultMigrationStrategyDAO dao = new DefaultMigrationStrategyDAO(storeFactory);
|
||||||
|
|
||||||
Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("id");
|
Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> entry = dao.get("id");
|
||||||
|
|
||||||
@@ -16,8 +16,6 @@ import sonia.scm.repository.Repository;
|
|||||||
import sonia.scm.repository.RepositoryPermission;
|
import sonia.scm.repository.RepositoryPermission;
|
||||||
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
||||||
import sonia.scm.store.ConfigurationEntryStore;
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
|
||||||
import sonia.scm.store.InMemoryConfigurationEntryStore;
|
|
||||||
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
|
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.update.UpdateStepTestUtil;
|
import sonia.scm.update.UpdateStepTestUtil;
|
||||||
|
|
||||||
@@ -49,7 +47,7 @@ class XmlRepositoryV1UpdateStepTest {
|
|||||||
@Mock
|
@Mock
|
||||||
XmlRepositoryDAO repositoryDAO;
|
XmlRepositoryDAO repositoryDAO;
|
||||||
@Mock
|
@Mock
|
||||||
MigrationStrategyDao migrationStrategyDao;
|
DefaultMigrationStrategyDAO migrationStrategyDao;
|
||||||
|
|
||||||
InMemoryConfigurationEntryStoreFactory configurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory();
|
InMemoryConfigurationEntryStoreFactory configurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory();
|
||||||
|
|
||||||
@@ -91,7 +89,7 @@ class XmlRepositoryV1UpdateStepTest {
|
|||||||
void createMigrationPlan() {
|
void createMigrationPlan() {
|
||||||
Answer<Object> planAnswer = invocation -> {
|
Answer<Object> planAnswer = invocation -> {
|
||||||
String id = invocation.getArgument(0).toString();
|
String id = invocation.getArgument(0).toString();
|
||||||
return of(new RepositoryMigrationPlan.RepositoryMigrationEntry(id, "originalName", MOVE, "namespace-" + id, "name-" + id));
|
return of(new RepositoryMigrationPlan.RepositoryMigrationEntry(id, "git", "originalName", MOVE, "namespace-" + id, "name-" + id));
|
||||||
};
|
};
|
||||||
|
|
||||||
lenient().when(migrationStrategyDao.get("3b91caa5-59c3-448f-920b-769aaa56b761")).thenAnswer(planAnswer);
|
lenient().when(migrationStrategyDao.get("3b91caa5-59c3-448f-920b-769aaa56b761")).thenAnswer(planAnswer);
|
||||||
|
|||||||
Reference in New Issue
Block a user