mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 17:26:22 +01:00
Merged in feature/updatestep_api_for_plugins (pull request #273)
Feature/updatestep api for plugins
This commit is contained in:
@@ -82,7 +82,7 @@ public interface BlobStoreFactory {
|
|||||||
final class FloatingStoreParameters implements StoreParameters {
|
final class FloatingStoreParameters implements StoreParameters {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private Repository repository;
|
private String repositoryId;
|
||||||
|
|
||||||
private final BlobStoreFactory factory;
|
private final BlobStoreFactory factory;
|
||||||
|
|
||||||
@@ -96,8 +96,8 @@ final class FloatingStoreParameters implements StoreParameters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Repository getRepository() {
|
public String getRepositoryId() {
|
||||||
return repository;
|
return repositoryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Builder {
|
public class Builder {
|
||||||
@@ -113,7 +113,18 @@ final class FloatingStoreParameters implements StoreParameters {
|
|||||||
* @return Floating API to finish the call.
|
* @return Floating API to finish the call.
|
||||||
*/
|
*/
|
||||||
public FloatingStoreParameters.Builder forRepository(Repository repository) {
|
public FloatingStoreParameters.Builder forRepository(Repository repository) {
|
||||||
FloatingStoreParameters.this.repository = repository;
|
FloatingStoreParameters.this.repositoryId = repository.getId();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to create or get a {@link BlobStore} for a specific repository. This step is optional. If you want to
|
||||||
|
* have a global {@link BlobStore}, omit this.
|
||||||
|
* @param repositoryId The id of the optional repository for the {@link BlobStore}.
|
||||||
|
* @return Floating API to finish the call.
|
||||||
|
*/
|
||||||
|
public FloatingStoreParameters.Builder forRepository(String repositoryId) {
|
||||||
|
FloatingStoreParameters.this.repositoryId = repositoryId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,18 @@ final class TypedFloatingConfigurationEntryStoreParameters<T> {
|
|||||||
* @return Floating API to finish the call.
|
* @return Floating API to finish the call.
|
||||||
*/
|
*/
|
||||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||||
parameters.setRepository(repository);
|
parameters.setRepositoryId(repository.getId());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to create or get a {@link ConfigurationEntryStore} for a specific repository. This step is optional. If
|
||||||
|
* you want to have a global {@link ConfigurationEntryStore}, omit this.
|
||||||
|
* @param repositoryId The id of the optional repository for the {@link ConfigurationEntryStore}.
|
||||||
|
* @return Floating API to finish the call.
|
||||||
|
*/
|
||||||
|
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||||
|
parameters.setRepositoryId(repositoryId);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,18 @@ final class TypedFloatingConfigurationStoreParameters<T> {
|
|||||||
* @return Floating API to finish the call.
|
* @return Floating API to finish the call.
|
||||||
*/
|
*/
|
||||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||||
parameters.setRepository(repository);
|
parameters.setRepositoryId(repository.getId());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to create or get a {@link ConfigurationStore} for a specific repository. This step is optional. If you
|
||||||
|
* want to have a global {@link ConfigurationStore}, omit this.
|
||||||
|
* @param repositoryId The id of the optional repository for the {@link ConfigurationStore}.
|
||||||
|
* @return Floating API to finish the call.
|
||||||
|
*/
|
||||||
|
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||||
|
parameters.setRepositoryId(repositoryId);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,18 @@ final class TypedFloatingDataStoreParameters<T> {
|
|||||||
* @return Floating API to finish the call.
|
* @return Floating API to finish the call.
|
||||||
*/
|
*/
|
||||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||||
parameters.setRepository(repository);
|
parameters.setRepositoryId(repository.getId());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to create or get a {@link DataStore} for a specific repository. This step is optional. If you
|
||||||
|
* want to have a global {@link DataStore}, omit this.
|
||||||
|
* @param repositoryId The id of the optional repository for the {@link DataStore}.
|
||||||
|
* @return Floating API to finish the call.
|
||||||
|
*/
|
||||||
|
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||||
|
parameters.setRepositoryId(repositoryId);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ public interface StoreParameters {
|
|||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
Repository getRepository();
|
String getRepositoryId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,5 @@ public interface TypedStoreParameters<T> {
|
|||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
Repository getRepository();
|
String getRepositoryId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package sonia.scm.store;
|
package sonia.scm.store;
|
||||||
|
|
||||||
import sonia.scm.repository.Repository;
|
|
||||||
|
|
||||||
class TypedStoreParametersImpl<T> implements TypedStoreParameters<T> {
|
class TypedStoreParametersImpl<T> implements TypedStoreParameters<T> {
|
||||||
private Class<T> type;
|
private Class<T> type;
|
||||||
private String name;
|
private String name;
|
||||||
private Repository repository;
|
private String repositoryId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<T> getType() {
|
public Class<T> getType() {
|
||||||
@@ -26,11 +24,11 @@ class TypedStoreParametersImpl<T> implements TypedStoreParameters<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Repository getRepository() {
|
public String getRepositoryId() {
|
||||||
return repository;
|
return repositoryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRepository(Repository repository) {
|
void setRepositoryId(String repositoryId) {
|
||||||
this.repository = repository;
|
this.repositoryId = repositoryId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GroupV1PropertyReader implements V1PropertyReader {
|
||||||
|
@Override
|
||||||
|
public String getStoreName() {
|
||||||
|
return "group-properties-v1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instance createInstance(Map<String, V1Properties> all) {
|
||||||
|
return new MapBasedPropertyReaderInstance(all);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class MapBasedPropertyReaderInstance implements V1PropertyReader.Instance {
|
||||||
|
private final Stream<Map.Entry<String, V1Properties>> all;
|
||||||
|
|
||||||
|
public MapBasedPropertyReaderInstance(Map<String, V1Properties> all) {
|
||||||
|
this(all.entrySet().stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MapBasedPropertyReaderInstance(Stream<Map.Entry<String, V1Properties>> all) {
|
||||||
|
this.all = all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEachEntry(BiConsumer<String, V1Properties> propertiesForNameConsumer) {
|
||||||
|
all.forEach(e -> call(e.getKey(), e.getValue(), propertiesForNameConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V1PropertyReader.Instance havingAnyOf(String... keys) {
|
||||||
|
return new MapBasedPropertyReaderInstance(all.filter(e -> e.getValue().hasAny(keys)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V1PropertyReader.Instance havingAllOf(String... keys) {
|
||||||
|
return new MapBasedPropertyReaderInstance(all.filter(e -> e.getValue().hasAll(keys)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void call(String repositoryId, V1Properties properties, BiConsumer<String, V1Properties> propertiesForNameConsumer) {
|
||||||
|
propertiesForNameConsumer.accept(repositoryId, properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public interface PropertyFileAccess {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to rename a configuration file.
|
||||||
|
* @param oldName The old file name.
|
||||||
|
* @return Object to specify the new file name.
|
||||||
|
*/
|
||||||
|
Target renameGlobalConfigurationFrom(String oldName);
|
||||||
|
|
||||||
|
interface Target {
|
||||||
|
/**
|
||||||
|
* Renames a file to the new name given here.
|
||||||
|
* @param newName The new file name.
|
||||||
|
* @throws IOException If the file could not be renamed.
|
||||||
|
*/
|
||||||
|
void to(String newName) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tool object for store migration from v1 to v2.
|
||||||
|
* @param name The name of the store to be handled.
|
||||||
|
* @return The tool object for the named store.
|
||||||
|
*/
|
||||||
|
StoreFileTools forStoreName(String name);
|
||||||
|
|
||||||
|
interface StoreFileTools {
|
||||||
|
/**
|
||||||
|
* Iterates over all found store files (that is, files ending with ".xml") and calls the
|
||||||
|
* given consumer for each file, giving the file and the name of the data file.
|
||||||
|
* @param storeFileConsumer This consumer will be called for each file found.
|
||||||
|
* @throws IOException May be thrown when an exception occurs.
|
||||||
|
*/
|
||||||
|
void forStoreFiles(FileConsumer storeFileConsumer) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a data file to the new location for a repository with the given id. If there
|
||||||
|
* is no directory for the given repository id, nothing will be done.
|
||||||
|
* @param storeFile The name of the store file.
|
||||||
|
* @param repositoryId The id of the repository as the new target for the store file.
|
||||||
|
* @throws IOException When the file could not be moved.
|
||||||
|
*/
|
||||||
|
void moveAsRepositoryStore(Path storeFile, String repositoryId) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileConsumer {
|
||||||
|
void accept(Path file, String storeName) throws IOException;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class RepositoryV1PropertyReader implements V1PropertyReader {
|
||||||
|
@Override
|
||||||
|
public String getStoreName() {
|
||||||
|
return "repository-properties-v1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instance createInstance(Map<String, V1Properties> all) {
|
||||||
|
return new MapBasedPropertyReaderInstance(all);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class UserV1PropertyReader implements V1PropertyReader {
|
||||||
|
@Override
|
||||||
|
public String getStoreName() {
|
||||||
|
return "user-properties-v1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instance createInstance(Map<String, V1Properties> all) {
|
||||||
|
return new MapBasedPropertyReaderInstance(all);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
scm-core/src/main/java/sonia/scm/update/V1Properties.java
Normal file
59
scm-core/src/main/java/sonia/scm/update/V1Properties.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
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.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
|
import static java.util.stream.Stream.empty;
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlRootElement(name = "properties")
|
||||||
|
public class V1Properties {
|
||||||
|
@XmlElement(name = "item")
|
||||||
|
private List<V1Property> properties;
|
||||||
|
|
||||||
|
public V1Properties() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public V1Properties(V1Property... properties) {
|
||||||
|
this(asList(properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
public V1Properties(List<V1Property> properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(String key) {
|
||||||
|
return getOptional(key).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getOptional(String key) {
|
||||||
|
return streamProps().filter(p -> key.equals(p.getKey())).map(V1Property::getValue).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Boolean> getBoolean(String key) {
|
||||||
|
return getOptional(key).map(Boolean::valueOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Enum<T>> Optional<T> getEnum(String key, Class<T> enumType) {
|
||||||
|
return getOptional(key).map(name -> Enum.valueOf(enumType, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAny(String[] keys) {
|
||||||
|
return streamProps().anyMatch(p -> stream(keys).anyMatch(k -> k.equals(p.getKey())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAll(String[] keys) {
|
||||||
|
return stream(keys).allMatch(k -> streamProps().anyMatch(p -> k.equals(p.getKey())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<V1Property> streamProps() {
|
||||||
|
return properties == null? empty(): properties.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
49
scm-core/src/main/java/sonia/scm/update/V1Property.java
Normal file
49
scm-core/src/main/java/sonia/scm/update/V1Property.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public class V1Property {
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public V1Property() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public V1Property(String key, String value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
V1Property that = (V1Property) o;
|
||||||
|
return Objects.equals(key, that.key) &&
|
||||||
|
Objects.equals(value, that.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "V1Property{" +
|
||||||
|
"key='" + key + '\'' +
|
||||||
|
", value='" + value + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
14
scm-core/src/main/java/sonia/scm/update/V1PropertyDAO.java
Normal file
14
scm-core/src/main/java/sonia/scm/update/V1PropertyDAO.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to access old properties from an instance of SCM-Manager v1.
|
||||||
|
*/
|
||||||
|
public interface V1PropertyDAO {
|
||||||
|
/**
|
||||||
|
* Creates an instance of a property reader to process old properties.
|
||||||
|
* @param reader The reader for the origin of the properties (for example
|
||||||
|
* {@link V1PropertyReader#REPOSITORY_PROPERTY_READER} for properties of repositories).
|
||||||
|
* @return The reader instance.
|
||||||
|
*/
|
||||||
|
V1PropertyReader.Instance getProperties(V1PropertyReader reader);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
public interface V1PropertyReader {
|
||||||
|
|
||||||
|
V1PropertyReader REPOSITORY_PROPERTY_READER = new RepositoryV1PropertyReader();
|
||||||
|
V1PropertyReader USER_PROPERTY_READER = new UserV1PropertyReader();
|
||||||
|
V1PropertyReader GROUP_PROPERTY_READER = new GroupV1PropertyReader();
|
||||||
|
|
||||||
|
String getStoreName();
|
||||||
|
|
||||||
|
Instance createInstance(Map<String, V1Properties> all);
|
||||||
|
|
||||||
|
interface Instance {
|
||||||
|
/**
|
||||||
|
* Will call the given consumer for each id of the corresponding entity with its list of
|
||||||
|
* properties converted from v1.
|
||||||
|
* For example for repositories this will call the consumer with the id of each repository
|
||||||
|
* that had properties attached in v1.
|
||||||
|
*/
|
||||||
|
void forEachEntry(BiConsumer<String, V1Properties> propertiesForNameConsumer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters for entities only having at least one property with a given key name.
|
||||||
|
*/
|
||||||
|
Instance havingAnyOf(String... keys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters for entities only having properties for all given key name.
|
||||||
|
*/
|
||||||
|
Instance havingAllOf(String... keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
class MapBasedPropertyReaderInstanceTest {
|
||||||
|
|
||||||
|
Map<String, V1Properties> executedCalls = new HashMap<>();
|
||||||
|
|
||||||
|
BiConsumer<String, V1Properties> consumer = (key, properties) -> executedCalls.put(key, properties);
|
||||||
|
|
||||||
|
MapBasedPropertyReaderInstance instance = new MapBasedPropertyReaderInstance(
|
||||||
|
ImmutableMap.of(
|
||||||
|
"o1", new V1Properties(
|
||||||
|
new V1Property("k1", "v1-1"),
|
||||||
|
new V1Property("k2", "v1-2"),
|
||||||
|
new V1Property("k3", "v1-3")
|
||||||
|
),
|
||||||
|
"o2", new V1Properties(
|
||||||
|
new V1Property("k1", "v2-1"),
|
||||||
|
new V1Property("k2", "v2-2")
|
||||||
|
),
|
||||||
|
"o3", new V1Properties(
|
||||||
|
new V1Property("k1", "v3-1")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCallBackForEachObjectIfNotFiltered() {
|
||||||
|
instance.forEachEntry(consumer);
|
||||||
|
|
||||||
|
Assertions.assertThat(executedCalls).hasSize(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCallBackOnlyObjectsHavingAtLeastOneOfGivenKey() {
|
||||||
|
instance.havingAnyOf("k2", "k3").forEachEntry(consumer);
|
||||||
|
|
||||||
|
Assertions.assertThat(executedCalls).hasSize(2).containsKeys("o1", "o2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCallBackOnlyObjectsHavingAllOfGivenKey() {
|
||||||
|
instance.havingAllOf("k2", "k3").forEachEntry(consumer);
|
||||||
|
|
||||||
|
Assertions.assertThat(executedCalls).hasSize(1).containsKeys("o1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCombineFilters() {
|
||||||
|
instance.havingAnyOf("k2", "k3").havingAllOf("k3").forEachEntry(consumer);
|
||||||
|
|
||||||
|
Assertions.assertThat(executedCalls).hasSize(1).containsKeys("o1");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,6 @@ package sonia.scm.store;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
import sonia.scm.repository.Repository;
|
|
||||||
import sonia.scm.repository.RepositoryLocationResolver;
|
import sonia.scm.repository.RepositoryLocationResolver;
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
@@ -66,18 +65,18 @@ public abstract class FileBasedStoreFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected File getStoreLocation(StoreParameters storeParameters) {
|
protected File getStoreLocation(StoreParameters storeParameters) {
|
||||||
return getStoreLocation(storeParameters.getName(), null, storeParameters.getRepository());
|
return getStoreLocation(storeParameters.getName(), null, storeParameters.getRepositoryId());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File getStoreLocation(TypedStoreParameters storeParameters) {
|
protected File getStoreLocation(TypedStoreParameters storeParameters) {
|
||||||
return getStoreLocation(storeParameters.getName(), storeParameters.getType(), storeParameters.getRepository());
|
return getStoreLocation(storeParameters.getName(), storeParameters.getType(), storeParameters.getRepositoryId());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File getStoreLocation(String name, Class type, Repository repository) {
|
protected File getStoreLocation(String name, Class type, String repositoryId) {
|
||||||
File storeDirectory;
|
File storeDirectory;
|
||||||
if (repository != null) {
|
if (repositoryId != null) {
|
||||||
LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName());
|
LOG.debug("create store with type: {}, name: {} and repository id: {}", type, name, repositoryId);
|
||||||
storeDirectory = this.getStoreDirectory(store, repository);
|
storeDirectory = this.getStoreDirectory(store, repositoryId);
|
||||||
} else {
|
} else {
|
||||||
LOG.debug("create store with type: {} and name: {} ", type, name);
|
LOG.debug("create store with type: {} and name: {} ", type, name);
|
||||||
storeDirectory = this.getStoreDirectory(store);
|
storeDirectory = this.getStoreDirectory(store);
|
||||||
@@ -89,11 +88,11 @@ public abstract class FileBasedStoreFactory {
|
|||||||
/**
|
/**
|
||||||
* Get the store directory of a specific repository
|
* Get the store directory of a specific repository
|
||||||
* @param store the type of the store
|
* @param store the type of the store
|
||||||
* @param repository the repo
|
* @param repositoryId the id of the repossitory
|
||||||
* @return the store directory of a specific repository
|
* @return the store directory of a specific repository
|
||||||
*/
|
*/
|
||||||
private File getStoreDirectory(Store store, Repository repository) {
|
private File getStoreDirectory(Store store, String repositoryId) {
|
||||||
return new File(repositoryLocationResolver.forClass(Path.class).getLocation(repository.getId()).toFile(), store.getRepositoryStoreDirectory());
|
return new File(repositoryLocationResolver.forClass(Path.class).getLocation(repositoryId).toFile(), store.getRepositoryStoreDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> ConfigurationEntryStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
public <T> ConfigurationEntryStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
||||||
return new JAXBConfigurationEntryStore<>(getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), storeParameters.getRepository()), keyGenerator, storeParameters.getType());
|
return new JAXBConfigurationEntryStore<>(
|
||||||
|
getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), storeParameters.getRepositoryId()),
|
||||||
|
keyGenerator,
|
||||||
|
storeParameters.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ public class JAXBConfigurationStoreFactory extends FileBasedStoreFactory impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> JAXBConfigurationStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
public <T> JAXBConfigurationStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
||||||
return new JAXBConfigurationStore<>(storeParameters.getType(), getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), storeParameters.getRepository()));
|
return new JAXBConfigurationStore<>(
|
||||||
|
storeParameters.getType(),
|
||||||
|
getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION),
|
||||||
|
storeParameters.getType(),
|
||||||
|
storeParameters.getRepositoryId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package sonia.scm.store;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.repository.RepositoryLocationResolver;
|
||||||
|
import sonia.scm.update.PropertyFileAccess;
|
||||||
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class JAXBPropertyFileAccess implements PropertyFileAccess {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JAXBPropertyFileAccess.class);
|
||||||
|
|
||||||
|
public static final String XML_FILENAME_SUFFIX = ".xml";
|
||||||
|
private final SCMContextProvider contextProvider;
|
||||||
|
private final RepositoryLocationResolver locationResolver;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JAXBPropertyFileAccess(SCMContextProvider contextProvider, RepositoryLocationResolver locationResolver) {
|
||||||
|
this.contextProvider = contextProvider;
|
||||||
|
this.locationResolver = locationResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Target renameGlobalConfigurationFrom(String oldName) {
|
||||||
|
return newName -> {
|
||||||
|
Path configDir = contextProvider.getBaseDirectory().toPath().resolve(StoreConstants.CONFIG_DIRECTORY_NAME);
|
||||||
|
Path oldConfigFile = configDir.resolve(oldName + XML_FILENAME_SUFFIX);
|
||||||
|
Path newConfigFile = configDir.resolve(newName + XML_FILENAME_SUFFIX);
|
||||||
|
Files.move(oldConfigFile, newConfigFile);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoreFileTools forStoreName(String storeName) {
|
||||||
|
return new StoreFileTools() {
|
||||||
|
@Override
|
||||||
|
public void forStoreFiles(FileConsumer storeFileConsumer) throws IOException {
|
||||||
|
Path v1storeDir = computeV1StoreDir();
|
||||||
|
if (Files.exists(v1storeDir) && Files.isDirectory(v1storeDir)) {
|
||||||
|
try (Stream<Path> fileStream = Files.list(v1storeDir)) {
|
||||||
|
fileStream.filter(p -> p.toString().endsWith(XML_FILENAME_SUFFIX)).forEach(p -> {
|
||||||
|
try {
|
||||||
|
String storeName = extractStoreName(p);
|
||||||
|
storeFileConsumer.accept(p, storeName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("could not call consumer for store file " + p + " with name " + storeName, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveAsRepositoryStore(Path storeFile, String repositoryId) throws IOException {
|
||||||
|
Path repositoryLocation;
|
||||||
|
try {
|
||||||
|
repositoryLocation = locationResolver
|
||||||
|
.forClass(Path.class)
|
||||||
|
.getLocation(repositoryId);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
LOG.info("ignoring store file {} because there is no repository location for repository id {}", storeFile, repositoryId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Path target = repositoryLocation
|
||||||
|
.resolve(Store.DATA.getRepositoryStoreDirectory())
|
||||||
|
.resolve(storeName);
|
||||||
|
IOUtil.mkdirs(target.toFile());
|
||||||
|
Path resolvedSourceFile = computeV1StoreDir().resolve(storeFile);
|
||||||
|
Path resolvedTargetFile = target.resolve(storeFile.getFileName());
|
||||||
|
LOG.trace("moving file {} to {}", resolvedSourceFile, resolvedTargetFile);
|
||||||
|
Files.move(resolvedSourceFile, resolvedTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path computeV1StoreDir() {
|
||||||
|
return contextProvider.getBaseDirectory().toPath().resolve("var").resolve("data").resolve(storeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractStoreName(Path p) {
|
||||||
|
String fileName = p.getFileName().toString();
|
||||||
|
return fileName.substring(0, fileName.length() - XML_FILENAME_SUFFIX.length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package sonia.scm.update.xml;
|
||||||
|
|
||||||
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
|
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||||
|
import sonia.scm.update.V1Properties;
|
||||||
|
import sonia.scm.update.V1PropertyDAO;
|
||||||
|
import sonia.scm.update.V1PropertyReader;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class XmlV1PropertyDAO implements V1PropertyDAO {
|
||||||
|
|
||||||
|
private final ConfigurationEntryStoreFactory configurationEntryStoreFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public XmlV1PropertyDAO(ConfigurationEntryStoreFactory configurationEntryStoreFactory) {
|
||||||
|
this.configurationEntryStoreFactory = configurationEntryStoreFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V1PropertyReader.Instance getProperties(V1PropertyReader reader) {
|
||||||
|
ConfigurationEntryStore<V1Properties> propertyStore = configurationEntryStoreFactory
|
||||||
|
.withType(V1Properties.class)
|
||||||
|
.withName(reader.getStoreName())
|
||||||
|
.build();
|
||||||
|
Map<String, V1Properties> all = propertyStore.getAll();
|
||||||
|
return reader.createInstance(all);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package sonia.scm.store;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junitpioneer.jupiter.TempDirectory;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.io.DefaultFileSystem;
|
||||||
|
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
||||||
|
import sonia.scm.repository.RepositoryLocationResolver;
|
||||||
|
import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver;
|
||||||
|
import sonia.scm.update.PropertyFileAccess;
|
||||||
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
|
||||||
|
@ExtendWith(TempDirectory.class)
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class JAXBPropertyFileAccessTest {
|
||||||
|
|
||||||
|
public static final String REPOSITORY_ID = "repoId";
|
||||||
|
public static final String STORE_NAME = "test";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
SCMContextProvider contextProvider;
|
||||||
|
|
||||||
|
RepositoryLocationResolver locationResolver;
|
||||||
|
|
||||||
|
JAXBPropertyFileAccess fileAccess;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void initTempDir(@TempDirectory.TempDir Path tempDir) {
|
||||||
|
lenient().when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
||||||
|
lenient().when(contextProvider.resolve(any())).thenAnswer(invocation -> tempDir.resolve(invocation.getArgument(0).toString()));
|
||||||
|
|
||||||
|
locationResolver = new PathBasedRepositoryLocationResolver(contextProvider, new InitialRepositoryLocationResolver(), new DefaultFileSystem());
|
||||||
|
|
||||||
|
fileAccess = new JAXBPropertyFileAccess(contextProvider, locationResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenameGlobalConfigFile() throws IOException {
|
||||||
|
Path baseDirectory = contextProvider.getBaseDirectory().toPath();
|
||||||
|
Path configDirectory = baseDirectory.resolve(StoreConstants.CONFIG_DIRECTORY_NAME);
|
||||||
|
|
||||||
|
Files.createDirectories(configDirectory);
|
||||||
|
|
||||||
|
Path oldPath = configDirectory.resolve("old" + StoreConstants.FILE_EXTENSION);
|
||||||
|
Files.createFile(oldPath);
|
||||||
|
|
||||||
|
fileAccess.renameGlobalConfigurationFrom("old").to("new");
|
||||||
|
|
||||||
|
Path newPath = configDirectory.resolve("new" + StoreConstants.FILE_EXTENSION);
|
||||||
|
assertThat(oldPath).doesNotExist();
|
||||||
|
assertThat(newPath).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ForExistingRepository {
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void createRepositoryLocation() {
|
||||||
|
locationResolver.forClass(Path.class).createLocation(REPOSITORY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMoveStoreFileToRepositoryBasedLocation(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||||
|
createV1StoreFile(tempDir, "myStore.xml");
|
||||||
|
|
||||||
|
fileAccess.forStoreName(STORE_NAME).moveAsRepositoryStore(Paths.get("myStore.xml"), REPOSITORY_ID);
|
||||||
|
|
||||||
|
assertThat(tempDir.resolve("repositories").resolve(REPOSITORY_ID).resolve("store").resolve("data").resolve(STORE_NAME).resolve("myStore.xml")).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMoveAllStoreFilesToRepositoryBasedLocations(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||||
|
locationResolver.forClass(Path.class).createLocation("repoId2");
|
||||||
|
|
||||||
|
createV1StoreFile(tempDir, REPOSITORY_ID + ".xml");
|
||||||
|
createV1StoreFile(tempDir, "repoId2.xml");
|
||||||
|
|
||||||
|
PropertyFileAccess.StoreFileTools statisticStoreAccess = fileAccess.forStoreName(STORE_NAME);
|
||||||
|
statisticStoreAccess.forStoreFiles(statisticStoreAccess::moveAsRepositoryStore);
|
||||||
|
|
||||||
|
assertThat(tempDir.resolve("repositories").resolve(REPOSITORY_ID).resolve("store").resolve("data").resolve(STORE_NAME).resolve("repoId.xml")).exists();
|
||||||
|
assertThat(tempDir.resolve("repositories").resolve("repoId2").resolve("store").resolve("data").resolve(STORE_NAME).resolve("repoId2.xml")).exists();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createV1StoreFile(@TempDirectory.TempDir Path tempDir, String name) throws IOException {
|
||||||
|
Path v1Dir = tempDir.resolve("var").resolve("data").resolve(STORE_NAME);
|
||||||
|
IOUtil.mkdirs(v1Dir.toFile());
|
||||||
|
Files.createFile(v1Dir.resolve(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ForMissingRepository {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldIgnoreStoreFile(@TempDirectory.TempDir Path tempDir) throws IOException {
|
||||||
|
createV1StoreFile(tempDir, "myStore.xml");
|
||||||
|
|
||||||
|
fileAccess.forStoreName(STORE_NAME).moveAsRepositoryStore(Paths.get("myStore.xml"), REPOSITORY_ID);
|
||||||
|
|
||||||
|
assertThat(tempDir.resolve("repositories").resolve(REPOSITORY_ID).resolve("store").resolve("data").resolve(STORE_NAME).resolve("myStore.xml")).doesNotExist();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,7 @@ import org.junit.Ignore;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -189,7 +190,7 @@ public class GitIncomingCommandTest
|
|||||||
*/
|
*/
|
||||||
private GitIncomingCommand createCommand()
|
private GitIncomingCommand createCommand()
|
||||||
{
|
{
|
||||||
return new GitIncomingCommand(handler, new GitContext(incomingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
return new GitIncomingCommand(handler, new GitContext(incomingDirectory, incomingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
||||||
incomingRepository);
|
this.incomingRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -160,7 +161,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
|||||||
*/
|
*/
|
||||||
private GitOutgoingCommand createCommand()
|
private GitOutgoingCommand createCommand()
|
||||||
{
|
{
|
||||||
return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, outgoingRepository, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
||||||
outgoingRepository);
|
outgoingRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class LfsBlobStoreFactoryTest {
|
|||||||
// the return value (and should not be part of this test)
|
// the return value (and should not be part of this test)
|
||||||
verify(blobStoreFactory).getStore(argThat(blobStoreParameters -> {
|
verify(blobStoreFactory).getStore(argThat(blobStoreParameters -> {
|
||||||
assertThat(blobStoreParameters.getName()).isEqualTo("the-id-git-lfs");
|
assertThat(blobStoreParameters.getName()).isEqualTo("the-id-git-lfs");
|
||||||
assertThat(blobStoreParameters.getRepository()).isEqualTo(repository);
|
assertThat(blobStoreParameters.getRepositoryId()).isEqualTo("the-id");
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
package sonia.scm.store;
|
package sonia.scm.store;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class InMemoryConfigurationEntryStoreFactory implements ConfigurationEntryStoreFactory {
|
public class InMemoryConfigurationEntryStoreFactory implements ConfigurationEntryStoreFactory {
|
||||||
|
|
||||||
|
private final Map<String, InMemoryConfigurationEntryStore> stores = new HashMap<>();
|
||||||
|
|
||||||
|
public static InMemoryConfigurationEntryStoreFactory create() {
|
||||||
|
return new InMemoryConfigurationEntryStoreFactory();
|
||||||
private ConfigurationEntryStore store;
|
|
||||||
|
|
||||||
public static ConfigurationEntryStoreFactory create() {
|
|
||||||
return new InMemoryConfigurationEntryStoreFactory(new InMemoryConfigurationEntryStore());
|
|
||||||
}
|
|
||||||
|
|
||||||
public InMemoryConfigurationEntryStoreFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public InMemoryConfigurationEntryStoreFactory(ConfigurationEntryStore store) {
|
|
||||||
this.store = store;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> ConfigurationEntryStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
public <T> ConfigurationEntryStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
||||||
if (store != null) {
|
String name = storeParameters.getName();
|
||||||
return store;
|
return get(name);
|
||||||
}
|
}
|
||||||
return new InMemoryConfigurationEntryStore<>();
|
|
||||||
|
public <T> InMemoryConfigurationEntryStore<T> get(String name) {
|
||||||
|
return stores.computeIfAbsent(name, x -> new InMemoryConfigurationEntryStore());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ package sonia.scm.store;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In memory configuration store factory for testing purposes.
|
* In memory configuration store factory for testing purposes.
|
||||||
*
|
*
|
||||||
@@ -44,24 +47,19 @@ package sonia.scm.store;
|
|||||||
*/
|
*/
|
||||||
public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFactory {
|
public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFactory {
|
||||||
|
|
||||||
private ConfigurationStore store;
|
private final Map<String, InMemoryConfigurationStore> stores = new HashMap<>();
|
||||||
|
|
||||||
public static ConfigurationStoreFactory create() {
|
public static InMemoryConfigurationStoreFactory create() {
|
||||||
return new InMemoryConfigurationStoreFactory(new InMemoryConfigurationStore());
|
return new InMemoryConfigurationStoreFactory();
|
||||||
}
|
|
||||||
|
|
||||||
public InMemoryConfigurationStoreFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public InMemoryConfigurationStoreFactory(ConfigurationStore store) {
|
|
||||||
this.store = store;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigurationStore getStore(TypedStoreParameters storeParameters) {
|
public ConfigurationStore getStore(TypedStoreParameters storeParameters) {
|
||||||
if (store != null) {
|
String name = storeParameters.getName();
|
||||||
return store;
|
return get(name);
|
||||||
}
|
}
|
||||||
return new InMemoryConfigurationStore<>();
|
|
||||||
|
public ConfigurationStore get(String name) {
|
||||||
|
return stores.computeIfAbsent(name, x -> new InMemoryConfigurationStore());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,6 @@ package sonia.scm.update;
|
|||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
import sonia.scm.security.AssignedPermission;
|
|
||||||
import sonia.scm.security.DefaultKeyGenerator;
|
|
||||||
import sonia.scm.store.ConfigurationEntryStore;
|
|
||||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
|
||||||
import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -20,15 +15,13 @@ import static org.mockito.Mockito.lenient;
|
|||||||
|
|
||||||
public class UpdateStepTestUtil {
|
public class UpdateStepTestUtil {
|
||||||
|
|
||||||
private final SCMContextProvider contextProvider;
|
private final SCMContextProvider contextProvider;
|
||||||
|
|
||||||
private final Path tempDir;
|
private final Path tempDir;
|
||||||
private final ConfigurationEntryStoreFactory storeFactory;
|
|
||||||
|
|
||||||
public UpdateStepTestUtil(Path tempDir) {
|
public UpdateStepTestUtil(Path tempDir) {
|
||||||
this.tempDir = tempDir;
|
this.tempDir = tempDir;
|
||||||
contextProvider = Mockito.mock(SCMContextProvider.class);
|
contextProvider = Mockito.mock(SCMContextProvider.class);
|
||||||
storeFactory = new JAXBConfigurationEntryStoreFactory(contextProvider, null, new DefaultKeyGenerator());
|
|
||||||
lenient().when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
lenient().when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
||||||
lenient().when(contextProvider.resolve(any())).thenAnswer(invocation -> tempDir.resolve(invocation.getArgument(0).toString()));
|
lenient().when(contextProvider.resolve(any())).thenAnswer(invocation -> tempDir.resolve(invocation.getArgument(0).toString()));
|
||||||
}
|
}
|
||||||
@@ -37,10 +30,6 @@ private final SCMContextProvider contextProvider;
|
|||||||
return contextProvider;
|
return contextProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationEntryStoreFactory getStoreFactory() {
|
|
||||||
return storeFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void copyConfigFile(String fileName) throws IOException {
|
public void copyConfigFile(String fileName) throws IOException {
|
||||||
Path configDir = tempDir.resolve("config");
|
Path configDir = tempDir.resolve("config");
|
||||||
Files.createDirectories(configDir);
|
Files.createDirectories(configDir);
|
||||||
@@ -53,17 +42,6 @@ private final SCMContextProvider contextProvider;
|
|||||||
copyTestDatabaseFile(configDir, fileName, targetFileName);
|
copyTestDatabaseFile(configDir, fileName, targetFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationEntryStore<AssignedPermission> getStoreForConfigFile(String name) {
|
|
||||||
return storeFactory
|
|
||||||
.withType(AssignedPermission.class)
|
|
||||||
.withName(name)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getFile(String name) {
|
|
||||||
return tempDir.resolve("config").resolve(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException {
|
private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException {
|
||||||
Path targetFileName = Paths.get(fileName).getFileName();
|
Path targetFileName = Paths.get(fileName).getFileName();
|
||||||
copyTestDatabaseFile(configDir, fileName, targetFileName.toString());
|
copyTestDatabaseFile(configDir, fileName, targetFileName.toString());
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class V1PropertyDaoTestUtil {
|
||||||
|
|
||||||
|
private final V1PropertyDAO propertyDAO = mock(V1PropertyDAO.class);
|
||||||
|
|
||||||
|
public V1PropertyDAO getPropertyDAO() {
|
||||||
|
return propertyDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mockRepositoryProperties(PropertiesForRepository... mockedPropertiesForRepositories) {
|
||||||
|
Map<String, V1Properties> map = new HashMap<>();
|
||||||
|
stream(mockedPropertiesForRepositories).forEach(p -> map.put(p.repositoryId, p.asProperties()));
|
||||||
|
V1PropertyReader.Instance v1PropertyReader = new MapBasedPropertyReaderInstance(map);
|
||||||
|
when(propertyDAO.getProperties(argThat(argument -> argument instanceof RepositoryV1PropertyReader))).thenReturn(v1PropertyReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PropertiesForRepository {
|
||||||
|
private final String repositoryId;
|
||||||
|
private final Map<String, String> properties;
|
||||||
|
|
||||||
|
public PropertiesForRepository(String repositoryId, Map<String, String> properties) {
|
||||||
|
this.repositoryId = repositoryId;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
V1Properties asProperties() {
|
||||||
|
return new V1Properties(properties.entrySet().stream().map(e -> new V1Property(e.getKey(), e.getValue())).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,10 @@ import sonia.scm.store.FileBlobStoreFactory;
|
|||||||
import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
|
import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
||||||
import sonia.scm.store.JAXBDataStoreFactory;
|
import sonia.scm.store.JAXBDataStoreFactory;
|
||||||
|
import sonia.scm.store.JAXBPropertyFileAccess;
|
||||||
|
import sonia.scm.update.PropertyFileAccess;
|
||||||
|
import sonia.scm.update.V1PropertyDAO;
|
||||||
|
import sonia.scm.update.xml.XmlV1PropertyDAO;
|
||||||
|
|
||||||
public class BootstrapModule extends AbstractModule {
|
public class BootstrapModule extends AbstractModule {
|
||||||
|
|
||||||
@@ -60,6 +64,8 @@ public class BootstrapModule extends AbstractModule {
|
|||||||
bind(DataStoreFactory.class, JAXBDataStoreFactory.class);
|
bind(DataStoreFactory.class, JAXBDataStoreFactory.class);
|
||||||
bind(BlobStoreFactory.class, FileBlobStoreFactory.class);
|
bind(BlobStoreFactory.class, FileBlobStoreFactory.class);
|
||||||
bind(PluginLoader.class).toInstance(pluginLoader);
|
bind(PluginLoader.class).toInstance(pluginLoader);
|
||||||
|
bind(V1PropertyDAO.class, XmlV1PropertyDAO.class);
|
||||||
|
bind(PropertyFileAccess.class, JAXBPropertyFileAccess.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void bind(Class<T> clazz, Class<? extends T> defaultImplementation) {
|
private <T> void bind(Class<T> clazz, Class<? extends T> defaultImplementation) {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package sonia.scm.update;
|
||||||
|
|
||||||
|
import sonia.scm.migration.UpdateStep;
|
||||||
|
|
||||||
|
public interface CoreUpdateStep extends UpdateStep {
|
||||||
|
}
|
||||||
@@ -32,12 +32,20 @@ public class UpdateEngine {
|
|||||||
private List<UpdateStep> sortSteps(Set<UpdateStep> steps) {
|
private List<UpdateStep> sortSteps(Set<UpdateStep> steps) {
|
||||||
LOG.trace("sorting available update steps:");
|
LOG.trace("sorting available update steps:");
|
||||||
List<UpdateStep> sortedSteps = steps.stream()
|
List<UpdateStep> sortedSteps = steps.stream()
|
||||||
.sorted(Comparator.comparing(UpdateStep::getTargetVersion).reversed())
|
.sorted(
|
||||||
|
Comparator
|
||||||
|
.comparing(UpdateStep::getTargetVersion)
|
||||||
|
.thenComparing(this::isCoreUpdateStep)
|
||||||
|
.reversed())
|
||||||
.collect(toList());
|
.collect(toList());
|
||||||
sortedSteps.forEach(step -> LOG.trace("{} for version {}", step.getAffectedDataType(), step.getTargetVersion()));
|
sortedSteps.forEach(step -> LOG.trace("{} for version {}", step.getAffectedDataType(), step.getTargetVersion()));
|
||||||
return sortedSteps;
|
return sortedSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCoreUpdateStep(UpdateStep updateStep) {
|
||||||
|
return updateStep instanceof CoreUpdateStep;
|
||||||
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
steps
|
steps
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import sonia.scm.plugin.Extension;
|
|||||||
import sonia.scm.store.ConfigurationEntryStore;
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.store.StoreConstants;
|
import sonia.scm.store.StoreConstants;
|
||||||
import sonia.scm.update.properties.V1Properties;
|
import sonia.scm.update.V1Properties;
|
||||||
import sonia.scm.version.Version;
|
import sonia.scm.version.Version;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -30,6 +30,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import static java.util.Optional.empty;
|
import static java.util.Optional.empty;
|
||||||
import static java.util.Optional.of;
|
import static java.util.Optional.of;
|
||||||
|
import static sonia.scm.update.V1PropertyReader.GROUP_PROPERTY_READER;
|
||||||
import static sonia.scm.version.Version.parse;
|
import static sonia.scm.version.Version.parse;
|
||||||
|
|
||||||
@Extension
|
@Extension
|
||||||
@@ -51,7 +52,7 @@ public class XmlGroupV1UpdateStep implements UpdateStep {
|
|||||||
this.groupDAO = groupDAO;
|
this.groupDAO = groupDAO;
|
||||||
this.propertyStore = configurationEntryStoreFactory
|
this.propertyStore = configurationEntryStoreFactory
|
||||||
.withType(V1Properties.class)
|
.withType(V1Properties.class)
|
||||||
.withName("group-properties-v1")
|
.withName(GROUP_PROPERTY_READER.getStoreName())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package sonia.scm.update.properties;
|
|
||||||
|
|
||||||
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.util.List;
|
|
||||||
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
|
||||||
@XmlRootElement(name = "properties")
|
|
||||||
public class V1Properties {
|
|
||||||
@XmlElement(name = "item")
|
|
||||||
private List<V1Property> properties;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package sonia.scm.update.properties;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
|
||||||
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
|
||||||
public class V1Property {
|
|
||||||
private String key;
|
|
||||||
private String value;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package sonia.scm.update.repository;
|
package sonia.scm.update.repository;
|
||||||
|
|
||||||
import sonia.scm.update.properties.V1Properties;
|
import sonia.scm.update.V1Properties;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
import sonia.scm.migration.UpdateException;
|
import sonia.scm.migration.UpdateException;
|
||||||
import sonia.scm.migration.UpdateStep;
|
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryPermission;
|
import sonia.scm.repository.RepositoryPermission;
|
||||||
@@ -13,7 +12,8 @@ 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.ConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.store.StoreConstants;
|
import sonia.scm.store.StoreConstants;
|
||||||
import sonia.scm.update.properties.V1Properties;
|
import sonia.scm.update.CoreUpdateStep;
|
||||||
|
import sonia.scm.update.V1Properties;
|
||||||
import sonia.scm.version.Version;
|
import sonia.scm.version.Version;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -36,6 +36,7 @@ import java.util.stream.Stream;
|
|||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Optional.empty;
|
import static java.util.Optional.empty;
|
||||||
import static java.util.Optional.of;
|
import static java.util.Optional.of;
|
||||||
|
import static sonia.scm.update.V1PropertyReader.REPOSITORY_PROPERTY_READER;
|
||||||
import static sonia.scm.version.Version.parse;
|
import static sonia.scm.version.Version.parse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,7 +57,7 @@ import static sonia.scm.version.Version.parse;
|
|||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@Extension
|
@Extension
|
||||||
public class XmlRepositoryV1UpdateStep implements UpdateStep {
|
public class XmlRepositoryV1UpdateStep implements CoreUpdateStep {
|
||||||
|
|
||||||
private static Logger LOG = LoggerFactory.getLogger(XmlRepositoryV1UpdateStep.class);
|
private static Logger LOG = LoggerFactory.getLogger(XmlRepositoryV1UpdateStep.class);
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
|
|||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.propertyStore = configurationEntryStoreFactory
|
this.propertyStore = configurationEntryStoreFactory
|
||||||
.withType(V1Properties.class)
|
.withType(V1Properties.class)
|
||||||
.withName("repository-properties-v1")
|
.withName(REPOSITORY_PROPERTY_READER.getStoreName())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import sonia.scm.security.AssignedPermission;
|
|||||||
import sonia.scm.store.ConfigurationEntryStore;
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.store.StoreConstants;
|
import sonia.scm.store.StoreConstants;
|
||||||
import sonia.scm.update.properties.V1Properties;
|
import sonia.scm.update.V1Properties;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.xml.XmlUserDAO;
|
import sonia.scm.user.xml.XmlUserDAO;
|
||||||
import sonia.scm.version.Version;
|
import sonia.scm.version.Version;
|
||||||
@@ -31,6 +31,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import static java.util.Optional.empty;
|
import static java.util.Optional.empty;
|
||||||
import static java.util.Optional.of;
|
import static java.util.Optional.of;
|
||||||
|
import static sonia.scm.update.V1PropertyReader.USER_PROPERTY_READER;
|
||||||
import static sonia.scm.version.Version.parse;
|
import static sonia.scm.version.Version.parse;
|
||||||
|
|
||||||
@Extension
|
@Extension
|
||||||
@@ -50,7 +51,7 @@ public class XmlUserV1UpdateStep implements UpdateStep {
|
|||||||
this.configurationEntryStoreFactory = configurationEntryStoreFactory;
|
this.configurationEntryStoreFactory = configurationEntryStoreFactory;
|
||||||
this.propertyStore = configurationEntryStoreFactory
|
this.propertyStore = configurationEntryStoreFactory
|
||||||
.withType(V1Properties.class)
|
.withType(V1Properties.class)
|
||||||
.withName("user-properties-v1")
|
.withName(USER_PROPERTY_READER.getStoreName())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import static sonia.scm.version.Version.parse;
|
|||||||
|
|
||||||
class UpdateEngineTest {
|
class UpdateEngineTest {
|
||||||
|
|
||||||
ConfigurationEntryStoreFactory storeFactory = new InMemoryConfigurationEntryStoreFactory(new InMemoryConfigurationEntryStore());
|
ConfigurationEntryStoreFactory storeFactory = new InMemoryConfigurationEntryStoreFactory();
|
||||||
|
|
||||||
List<String> processedUpdates = new ArrayList<>();
|
List<String> processedUpdates = new ArrayList<>();
|
||||||
|
|
||||||
@@ -32,7 +32,21 @@ class UpdateEngineTest {
|
|||||||
updateEngine.update();
|
updateEngine.update();
|
||||||
|
|
||||||
assertThat(processedUpdates)
|
assertThat(processedUpdates)
|
||||||
.containsExactly("1.1.0", "1.1.1", "1.2.0");
|
.containsExactly("test:1.1.0", "test:1.1.1", "test:1.2.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldProcessCoreStepsBeforeOther() {
|
||||||
|
LinkedHashSet<UpdateStep> updateSteps = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
updateSteps.add(new FixedVersionUpdateStep("test", "1.2.0"));
|
||||||
|
updateSteps.add(new CoreFixedVersionUpdateStep("core", "1.2.0"));
|
||||||
|
|
||||||
|
UpdateEngine updateEngine = new UpdateEngine(updateSteps, storeFactory);
|
||||||
|
updateEngine.update();
|
||||||
|
|
||||||
|
assertThat(processedUpdates)
|
||||||
|
.containsExactly("core:1.2.0", "test:1.2.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -67,7 +81,7 @@ class UpdateEngineTest {
|
|||||||
updateEngine = new UpdateEngine(updateSteps, storeFactory);
|
updateEngine = new UpdateEngine(updateSteps, storeFactory);
|
||||||
updateEngine.update();
|
updateEngine.update();
|
||||||
|
|
||||||
assertThat(processedUpdates).containsExactly("1.1.1");
|
assertThat(processedUpdates).containsExactly("other:1.1.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
class FixedVersionUpdateStep implements UpdateStep {
|
class FixedVersionUpdateStep implements UpdateStep {
|
||||||
@@ -91,7 +105,13 @@ class UpdateEngineTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doUpdate() {
|
public void doUpdate() {
|
||||||
processedUpdates.add(version);
|
processedUpdates.add(type + ":" + version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoreFixedVersionUpdateStep extends FixedVersionUpdateStep implements CoreUpdateStep {
|
||||||
|
CoreFixedVersionUpdateStep(String type, String version) {
|
||||||
|
super(type, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import sonia.scm.group.Group;
|
import sonia.scm.group.Group;
|
||||||
import sonia.scm.group.xml.XmlGroupDAO;
|
import sonia.scm.group.xml.XmlGroupDAO;
|
||||||
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.update.UpdateStepTestUtil;
|
import sonia.scm.update.UpdateStepTestUtil;
|
||||||
|
import sonia.scm.update.V1Properties;
|
||||||
|
import sonia.scm.update.V1Property;
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
import javax.xml.bind.JAXBException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -20,11 +24,11 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.linesOf;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static sonia.scm.store.InMemoryConfigurationEntryStoreFactory.create;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@ExtendWith(TempDirectory.class)
|
@ExtendWith(TempDirectory.class)
|
||||||
@@ -36,6 +40,8 @@ class XmlGroupV1UpdateStepTest {
|
|||||||
@Captor
|
@Captor
|
||||||
ArgumentCaptor<Group> groupCaptor;
|
ArgumentCaptor<Group> groupCaptor;
|
||||||
|
|
||||||
|
InMemoryConfigurationEntryStoreFactory storeFactory = create();
|
||||||
|
|
||||||
XmlGroupV1UpdateStep updateStep;
|
XmlGroupV1UpdateStep updateStep;
|
||||||
|
|
||||||
private UpdateStepTestUtil testUtil;
|
private UpdateStepTestUtil testUtil;
|
||||||
@@ -44,7 +50,7 @@ class XmlGroupV1UpdateStepTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
|
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
|
||||||
testUtil = new UpdateStepTestUtil(tempDir);
|
testUtil = new UpdateStepTestUtil(tempDir);
|
||||||
updateStep = new XmlGroupV1UpdateStep(testUtil.getContextProvider(), groupDAO, testUtil.getStoreFactory());
|
updateStep = new XmlGroupV1UpdateStep(testUtil.getContextProvider(), groupDAO, storeFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@@ -83,19 +89,10 @@ class XmlGroupV1UpdateStepTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldExtractProperties() throws JAXBException {
|
void shouldExtractProperties() throws JAXBException {
|
||||||
updateStep.doUpdate();
|
updateStep.doUpdate();
|
||||||
Path propertiesFile = testUtil.getFile("group-properties-v1.xml");
|
ConfigurationEntryStore<V1Properties> propertiesStore = storeFactory.get("group-properties-v1");
|
||||||
assertThat(propertiesFile)
|
V1Properties properties = propertiesStore.get("normals");
|
||||||
.exists();
|
assertThat(properties).isNotNull();
|
||||||
assertThat(linesOf(propertiesFile.toFile()))
|
assertThat(properties.get("mostly")).isEqualTo("humans");
|
||||||
.extracting(String::trim)
|
|
||||||
.containsSequence(
|
|
||||||
"<key>normals</key>",
|
|
||||||
"<value>",
|
|
||||||
"<item>",
|
|
||||||
"<key>mostly</key>",
|
|
||||||
"<value>humans</value>",
|
|
||||||
"</item>",
|
|
||||||
"</value>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class XmlRepositoryV1UpdateStepTest {
|
|||||||
@Mock
|
@Mock
|
||||||
MigrationStrategyDao migrationStrategyDao;
|
MigrationStrategyDao migrationStrategyDao;
|
||||||
|
|
||||||
ConfigurationEntryStoreFactory configurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory(new InMemoryConfigurationEntryStore());
|
InMemoryConfigurationEntryStoreFactory configurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory();
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
ArgumentCaptor<Repository> storeCaptor;
|
ArgumentCaptor<Repository> storeCaptor;
|
||||||
@@ -137,7 +137,7 @@ class XmlRepositoryV1UpdateStepTest {
|
|||||||
void shouldExtractPropertiesFromRepositories() throws JAXBException {
|
void shouldExtractPropertiesFromRepositories() throws JAXBException {
|
||||||
updateStep.doUpdate();
|
updateStep.doUpdate();
|
||||||
|
|
||||||
ConfigurationEntryStore<Object> store = configurationEntryStoreFactory.withType(null).withName("").build();
|
ConfigurationEntryStore store = configurationEntryStoreFactory.get("repository-properties-v1");
|
||||||
assertThat(store.getAll())
|
assertThat(store.getAll())
|
||||||
.hasSize(3);
|
.hasSize(3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.util.List;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static sonia.scm.store.InMemoryConfigurationEntryStoreFactory.create;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@ExtendWith(TempDirectory.class)
|
@ExtendWith(TempDirectory.class)
|
||||||
@@ -40,8 +41,8 @@ class XmlSecurityV1UpdateStepTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
|
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
|
||||||
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
|
||||||
assignedPermissionStore = new InMemoryConfigurationEntryStore<>();
|
InMemoryConfigurationEntryStoreFactory inMemoryConfigurationEntryStoreFactory = create();
|
||||||
ConfigurationEntryStoreFactory inMemoryConfigurationEntryStoreFactory = new InMemoryConfigurationEntryStoreFactory(assignedPermissionStore);
|
assignedPermissionStore = inMemoryConfigurationEntryStoreFactory.get("security");
|
||||||
updateStep = new XmlSecurityV1UpdateStep(contextProvider, inMemoryConfigurationEntryStoreFactory);
|
updateStep = new XmlSecurityV1UpdateStep(contextProvider, inMemoryConfigurationEntryStoreFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import org.mockito.Captor;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import sonia.scm.security.AssignedPermission;
|
import sonia.scm.security.AssignedPermission;
|
||||||
|
import sonia.scm.store.ConfigurationEntryStore;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
|
||||||
import sonia.scm.update.UpdateStepTestUtil;
|
import sonia.scm.update.UpdateStepTestUtil;
|
||||||
|
import sonia.scm.update.V1Properties;
|
||||||
|
import sonia.scm.update.V1Property;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.xml.XmlUserDAO;
|
import sonia.scm.user.xml.XmlUserDAO;
|
||||||
|
|
||||||
@@ -20,11 +24,11 @@ import java.nio.file.Path;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.linesOf;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static sonia.scm.store.InMemoryConfigurationEntryStoreFactory.create;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@ExtendWith(TempDirectory.class)
|
@ExtendWith(TempDirectory.class)
|
||||||
@@ -36,6 +40,8 @@ class XmlUserV1UpdateStepTest {
|
|||||||
@Captor
|
@Captor
|
||||||
ArgumentCaptor<User> userCaptor;
|
ArgumentCaptor<User> userCaptor;
|
||||||
|
|
||||||
|
InMemoryConfigurationEntryStoreFactory storeFactory = create();
|
||||||
|
|
||||||
XmlUserV1UpdateStep updateStep;
|
XmlUserV1UpdateStep updateStep;
|
||||||
|
|
||||||
private UpdateStepTestUtil testUtil;
|
private UpdateStepTestUtil testUtil;
|
||||||
@@ -43,7 +49,7 @@ class XmlUserV1UpdateStepTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
|
void mockScmHome(@TempDirectory.TempDir Path tempDir) {
|
||||||
testUtil = new UpdateStepTestUtil(tempDir);
|
testUtil = new UpdateStepTestUtil(tempDir);
|
||||||
updateStep = new XmlUserV1UpdateStep(testUtil.getContextProvider(), userDAO, testUtil.getStoreFactory());
|
updateStep = new XmlUserV1UpdateStep(testUtil.getContextProvider(), userDAO, storeFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@@ -63,7 +69,7 @@ class XmlUserV1UpdateStepTest {
|
|||||||
void shouldCreateNewPermissionsForV1AdminUser() throws JAXBException {
|
void shouldCreateNewPermissionsForV1AdminUser() throws JAXBException {
|
||||||
updateStep.doUpdate();
|
updateStep.doUpdate();
|
||||||
Optional<AssignedPermission> assignedPermission =
|
Optional<AssignedPermission> assignedPermission =
|
||||||
testUtil.getStoreForConfigFile("security")
|
storeFactory.<AssignedPermission>get("security")
|
||||||
.getAll()
|
.getAll()
|
||||||
.values()
|
.values()
|
||||||
.stream()
|
.stream()
|
||||||
@@ -98,23 +104,11 @@ class XmlUserV1UpdateStepTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldExtractProperties() throws JAXBException {
|
void shouldExtractProperties() throws JAXBException {
|
||||||
updateStep.doUpdate();
|
updateStep.doUpdate();
|
||||||
Path propertiesFile = testUtil.getFile("user-properties-v1.xml");
|
ConfigurationEntryStore<V1Properties> propertiesStore = storeFactory.<V1Properties>get("user-properties-v1");
|
||||||
assertThat(propertiesFile)
|
V1Properties properties = propertiesStore.get("dent");
|
||||||
.exists();
|
assertThat(properties).isNotNull();
|
||||||
assertThat(linesOf(propertiesFile.toFile()))
|
assertThat(properties.get("born.on")).isEqualTo("earth");
|
||||||
.extracting(String::trim)
|
assertThat(properties.get("last.seen")).isEqualTo("end of the universe");
|
||||||
.containsSequence(
|
|
||||||
"<key>dent</key>",
|
|
||||||
"<value>",
|
|
||||||
"<item>",
|
|
||||||
"<key>born.on</key>",
|
|
||||||
"<value>earth</value>",
|
|
||||||
"</item>",
|
|
||||||
"<item>",
|
|
||||||
"<key>last.seen</key>",
|
|
||||||
"<value>end of the universe</value>",
|
|
||||||
"</item>",
|
|
||||||
"</value>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user