mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
Create v2 repository entities for old v1 repositories
This commit is contained in:
@@ -30,7 +30,7 @@ import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
|||||||
*/
|
*/
|
||||||
public class PathBasedRepositoryLocationResolver extends BasicRepositoryLocationResolver<Path> {
|
public class PathBasedRepositoryLocationResolver extends BasicRepositoryLocationResolver<Path> {
|
||||||
|
|
||||||
private static final String STORE_NAME = "repositories";
|
private static final String STORE_NAME = "repository-paths";
|
||||||
|
|
||||||
private final SCMContextProvider contextProvider;
|
private final SCMContextProvider contextProvider;
|
||||||
private final InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
private final InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
||||||
|
|||||||
@@ -153,7 +153,12 @@ public abstract class ZippedRepositoryTestBase extends AbstractTestBase
|
|||||||
*/
|
*/
|
||||||
private void extract(File folder) throws IOException
|
private void extract(File folder) throws IOException
|
||||||
{
|
{
|
||||||
URL url = Resources.getResource(getZippedRepositoryResource());
|
String zippedRepositoryResource = getZippedRepositoryResource();
|
||||||
|
extract(folder, zippedRepositoryResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void extract(File targetFolder, String zippedRepositoryResource) throws IOException {
|
||||||
|
URL url = Resources.getResource(zippedRepositoryResource);
|
||||||
ZipInputStream zip = null;
|
ZipInputStream zip = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -164,7 +169,7 @@ public abstract class ZippedRepositoryTestBase extends AbstractTestBase
|
|||||||
|
|
||||||
while (entry != null)
|
while (entry != null)
|
||||||
{
|
{
|
||||||
File file = new File(folder, entry.getName());
|
File file = new File(targetFolder, entry.getName());
|
||||||
File parent = file.getParentFile();
|
File parent = file.getParentFile();
|
||||||
|
|
||||||
if (!parent.exists())
|
if (!parent.exists())
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package sonia.scm.repository.update;
|
||||||
|
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.io.FileSystem;
|
||||||
|
import sonia.scm.migration.UpdateStep;
|
||||||
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryPermission;
|
||||||
|
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
||||||
|
import sonia.scm.store.StoreConstants;
|
||||||
|
import sonia.scm.version.Version;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static sonia.scm.version.Version.parse;
|
||||||
|
|
||||||
|
@Extension
|
||||||
|
public class XmlRepositoryV1UpdateStep implements UpdateStep {
|
||||||
|
|
||||||
|
private final SCMContextProvider contextProvider;
|
||||||
|
private final XmlRepositoryDAO dao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public XmlRepositoryV1UpdateStep(SCMContextProvider contextProvider, XmlRepositoryDAO dao) {
|
||||||
|
this.contextProvider = contextProvider;
|
||||||
|
this.dao = dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getTargetVersion() {
|
||||||
|
return parse("0.0.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAffectedDataType() {
|
||||||
|
return "sonia.scm.repository.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doUpdate() throws JAXBException {
|
||||||
|
JAXBContext jaxbContext = JAXBContext.newInstance(V1RepositoryDatabase.class);
|
||||||
|
V1RepositoryDatabase v1Database = readV1Database(jaxbContext);
|
||||||
|
v1Database.repositoryList.repositories.forEach(this::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(V1Repository v1Repository) {
|
||||||
|
Repository repository = new Repository(
|
||||||
|
v1Repository.id,
|
||||||
|
v1Repository.type,
|
||||||
|
getNamespace(v1Repository),
|
||||||
|
getName(v1Repository),
|
||||||
|
v1Repository.contact,
|
||||||
|
v1Repository.description,
|
||||||
|
createPermissions(v1Repository));
|
||||||
|
dao.add(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RepositoryPermission[] createPermissions(V1Repository v1Repository) {
|
||||||
|
if (v1Repository.permissions == null) {
|
||||||
|
return new RepositoryPermission[0];
|
||||||
|
}
|
||||||
|
return v1Repository.permissions
|
||||||
|
.stream()
|
||||||
|
.map(this::createPermission)
|
||||||
|
.toArray(RepositoryPermission[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RepositoryPermission createPermission(V1Permission v1Permission) {
|
||||||
|
return new RepositoryPermission(v1Permission.name, v1Permission.type, v1Permission.groupPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNamespace(V1Repository v1Repository) {
|
||||||
|
String[] nameParts = getNameParts(v1Repository.name);
|
||||||
|
return nameParts.length > 1? nameParts[0]: v1Repository.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getName(V1Repository v1Repository) {
|
||||||
|
String[] nameParts = getNameParts(v1Repository.name);
|
||||||
|
return nameParts.length == 1? nameParts[0]: concatPathElements(nameParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String concatPathElements(String[] nameParts) {
|
||||||
|
return Arrays.stream(nameParts).skip(1).collect(Collectors.joining("_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getNameParts(String v1Name) {
|
||||||
|
return v1Name.split("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
private V1RepositoryDatabase readV1Database(JAXBContext jaxbContext) throws JAXBException {
|
||||||
|
return (V1RepositoryDatabase) jaxbContext.createUnmarshaller().unmarshal(determineV1File());
|
||||||
|
}
|
||||||
|
|
||||||
|
private File determineV1File() {
|
||||||
|
File configDirectory = new File(contextProvider.getBaseDirectory(), StoreConstants.CONFIG_DIRECTORY_NAME);
|
||||||
|
return new File(configDirectory, "repositories" + StoreConstants.FILE_EXTENSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlRootElement(name = "permissions")
|
||||||
|
public static class V1Permission {
|
||||||
|
private boolean groupPermission;
|
||||||
|
private String name;
|
||||||
|
private String type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlRootElement(name = "repositories")
|
||||||
|
public static class V1Repository {
|
||||||
|
private Map<String, String> properties;
|
||||||
|
private String contact;
|
||||||
|
private long creationDate;
|
||||||
|
private Long lastModified;
|
||||||
|
private String description;
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private boolean isPublic;
|
||||||
|
private boolean archived;
|
||||||
|
private String type;
|
||||||
|
private List<V1Permission> permissions;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "V1Repository{" +
|
||||||
|
"properties=" + properties +
|
||||||
|
", contact='" + contact + '\'' +
|
||||||
|
", creationDate=" + creationDate +
|
||||||
|
", lastModified=" + lastModified +
|
||||||
|
", description='" + description + '\'' +
|
||||||
|
", id='" + id + '\'' +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", isPublic=" + isPublic +
|
||||||
|
", archived=" + archived +
|
||||||
|
", type='" + type + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RepositoryList {
|
||||||
|
@XmlElement(name = "repository")
|
||||||
|
private List<V1Repository> repositories;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement(name = "repository-db")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public static class V1RepositoryDatabase {
|
||||||
|
private long creationTime;
|
||||||
|
private Long lastModified;
|
||||||
|
@XmlElement(name = "repositories")
|
||||||
|
private RepositoryList repositoryList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package sonia.scm.repository.update;
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junitpioneer.jupiter.TempDirectory;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryPermission;
|
||||||
|
import sonia.scm.repository.spi.ZippedRepositoryTestBase;
|
||||||
|
import sonia.scm.repository.xml.XmlRepositoryDAO;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@ExtendWith(TempDirectory.class)
|
||||||
|
class XmlRepositoryV1UpdateStepTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
SCMContextProvider contextProvider;
|
||||||
|
@Mock
|
||||||
|
XmlRepositoryDAO dao;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<Repository> storeCaptor;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
XmlRepositoryV1UpdateStep updateStep;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void createV1Home(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||||
|
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
||||||
|
ZippedRepositoryTestBase.extract(tempDir.toFile(), "sonia/scm/repository/update/scm-home.v1.zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void captureStoredRepositories() {
|
||||||
|
doNothing().when(dao).add(storeCaptor.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCreateNewRepositories() throws JAXBException {
|
||||||
|
updateStep.doUpdate();
|
||||||
|
verify(dao, times(3)).add(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMapAttributes() throws JAXBException {
|
||||||
|
updateStep.doUpdate();
|
||||||
|
|
||||||
|
Optional<Repository> repository = findByNamespace("git");
|
||||||
|
|
||||||
|
assertThat(repository)
|
||||||
|
.get()
|
||||||
|
.hasFieldOrPropertyWithValue("type", "git")
|
||||||
|
.hasFieldOrPropertyWithValue("contact", "arthur@dent.uk")
|
||||||
|
.hasFieldOrPropertyWithValue("description", "A simple repository without directories.")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseRepositoryTypeAsNamespaceForNamesWithSingleElement() throws JAXBException {
|
||||||
|
updateStep.doUpdate();
|
||||||
|
|
||||||
|
Optional<Repository> repository = findByNamespace("git");
|
||||||
|
|
||||||
|
assertThat(repository)
|
||||||
|
.get()
|
||||||
|
.hasFieldOrPropertyWithValue("namespace", "git")
|
||||||
|
.hasFieldOrPropertyWithValue("name", "simple")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseDirectoriesForNamespaceAndNameForNamesWithTwoElements() throws JAXBException {
|
||||||
|
updateStep.doUpdate();
|
||||||
|
|
||||||
|
Optional<Repository> repository = findByNamespace("one");
|
||||||
|
|
||||||
|
assertThat(repository)
|
||||||
|
.get()
|
||||||
|
.hasFieldOrPropertyWithValue("namespace", "one")
|
||||||
|
.hasFieldOrPropertyWithValue("name", "directory")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseDirectoriesForNamespaceAndConcatenatedNameForNamesWithMoreThanTwoElements() throws JAXBException {
|
||||||
|
updateStep.doUpdate();
|
||||||
|
|
||||||
|
Optional<Repository> repository = findByNamespace("some");
|
||||||
|
|
||||||
|
assertThat(repository)
|
||||||
|
.get()
|
||||||
|
.hasFieldOrPropertyWithValue("namespace", "some")
|
||||||
|
.hasFieldOrPropertyWithValue("name", "more_directories_than_one")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMapPermissions() throws JAXBException {
|
||||||
|
updateStep.doUpdate();
|
||||||
|
|
||||||
|
Optional<Repository> repository = findByNamespace("git");
|
||||||
|
|
||||||
|
assertThat(repository.get().getPermissions())
|
||||||
|
.hasSize(3)
|
||||||
|
.contains(
|
||||||
|
new RepositoryPermission("mice", "WRITE", true),
|
||||||
|
new RepositoryPermission("dent", "OWNER", false),
|
||||||
|
new RepositoryPermission("trillian", "READ", false)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Repository> findByNamespace(String namespace) {
|
||||||
|
return storeCaptor.getAllValues().stream().filter(r -> r.getNamespace().equals(namespace)).findFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user