package com.constellio.model.services.configs;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Before;
import org.junit.Test;
import com.constellio.app.entities.modules.InstallableModule;
import com.constellio.app.entities.modules.MigrationResourcesProvider;
import com.constellio.app.entities.modules.MigrationScript;
import com.constellio.app.entities.navigation.NavigationConfig;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.data.io.streamFactories.StreamFactory;
import com.constellio.data.utils.Delayed;
import com.constellio.model.entities.calculators.CalculatorParameters;
import com.constellio.model.entities.calculators.MetadataValueCalculator;
import com.constellio.model.entities.calculators.dependencies.ConfigDependency;
import com.constellio.model.entities.calculators.dependencies.Dependency;
import com.constellio.model.entities.configs.SystemConfiguration;
import com.constellio.model.entities.configs.SystemConfigurationGroup;
import com.constellio.model.entities.configs.SystemConfigurationScript;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataValueType;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.frameworks.validation.ValidationErrors;
import com.constellio.model.services.configs.SystemConfigurationsManagerRuntimeException.SystemConfigurationsManagerRuntimeException_InvalidConfigValue;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.migrations.ConstellioEIMConfigs;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.MetadataSchemasManagerException.OptimisticLocking;
import com.constellio.model.services.schemas.builders.MetadataBuilder_EnumClassTest.AValidEnum;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypesBuilder;
import com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators;
import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition;
import com.constellio.sdk.tests.ConstellioTest;
import com.constellio.sdk.tests.annotations.SlowTest;
@SlowTest
public class SystemConfigurationsManagerAcceptanceTest extends ConstellioTest {
static AtomicInteger callCollectionsActionCallCount;
String anotherCollection = "anotherCollection";
String aThirdCollection = "aThirdCollection";
static SystemConfigurationGroup aGroup = new SystemConfigurationGroup("zeModule", "aGroup");
static SystemConfigurationGroup anOtherGroup = new SystemConfigurationGroup("zeModule", "anotherGroup");
static SystemConfiguration numberUsedByCalculators = aGroup.createInteger("numberUsedByCalculators").withDefaultValue(42);
static SystemConfiguration textAlteringSchemas = aGroup.createString("textAlteringSchemas").scriptedBy(
TextAlteringSchemasScript.class).withDefaultValue("ohHellNo");
static SystemConfiguration text = aGroup.createString("text");
static SystemConfiguration textWithDefaultValue = aGroup.createString("textWithDefaultValue").withDefaultValue("bob");
static SystemConfiguration booleanWithTrueByDefault = aGroup.createBooleanTrueByDefault("booleanWithTrueByDefault");
static SystemConfiguration booleanWithFalseByDefault = aGroup.createBooleanFalseByDefault("booleanWithFalseByDefault");
static SystemConfiguration binary = aGroup.createBinary("binary");
static SystemConfiguration number = aGroup.createInteger("number");
static SystemConfiguration numberWithDefaultValue = aGroup.createInteger("numberWithDefaultValue").withDefaultValue(42);
static SystemConfiguration enumValue = anOtherGroup.createEnum("enumValue", AValidEnum.class);
static SystemConfiguration enumWithDefaultValue = anOtherGroup.createEnum("enumWithDefaultValue", AValidEnum.class)
.withDefaultValue(AValidEnum.FIRST_VALUE);
static SystemConfigurationsManager manager;
@Before
public void setUp()
throws Exception {
givenSpecialCollection(zeCollection).withModule(ZeModule.class).withAllTestUsers();
givenSpecialCollection(anotherCollection).withModule(ZeModule.class).withAllTestUsers();
givenSpecialCollection(aThirdCollection).withAllTestUsers();
manager = getModelLayerFactory().getSystemConfigurationsManager();
callCollectionsActionCallCount = new AtomicInteger();
}
@Test
public void whenGetConfigurationGroupsAndConfigurationsThenReturnInstalledModulesConfigurations()
throws Exception {
assertThat(manager.getConfigurationGroups()).containsOnlyOnce(
new SystemConfigurationGroup("zeModule", "aGroup"),
new SystemConfigurationGroup("zeModule", "anotherGroup"));
assertThat(manager.getGroupConfigurations(new SystemConfigurationGroup("zeModule", "aGroup"))).containsOnlyOnce(
text, textWithDefaultValue, booleanWithTrueByDefault, booleanWithFalseByDefault, number, numberWithDefaultValue
);
assertThat(manager.getGroupConfigurations(new SystemConfigurationGroup("zeModule", "anotherGroup"))).containsOnlyOnce(
enumValue, enumWithDefaultValue
);
}
@Test
public void whenInitializingThenConfigsLoaded()
throws Exception {
manager.setValue(text, "dakota");
SystemConfigurationsManager otherManager = new SystemConfigurationsManager(getModelLayerFactory(),
getDataLayerFactory().getConfigManager(), new Delayed<>(getAppLayerFactory().getModulesManager()));
otherManager.initialize();
assertThat(otherManager.getValue(text)).isEqualTo("dakota");
assertThat(otherManager.getValue(textWithDefaultValue)).isEqualTo("bob");
}
@Test
public void givenTextMetadataThenCanRetrieveAndAlterValue()
throws Exception {
assertThat(manager.getValue(text)).isNull();
assertThat(manager.getValue(textWithDefaultValue)).isEqualTo("bob");
manager.setValue(text, "dakota");
manager.setValue(textWithDefaultValue, "lindien");
assertThat(manager.getValue(text)).isEqualTo("dakota");
assertThat(manager.getValue(textWithDefaultValue)).isEqualTo("lindien");
manager.setValue(text, "alice");
manager.setValue(textWithDefaultValue, "wonderland");
assertThat(manager.getValue(text)).isEqualTo("alice");
assertThat(manager.getValue(textWithDefaultValue)).isEqualTo("wonderland");
manager.reset(text);
manager.reset(textWithDefaultValue);
assertThat(manager.getValue(text)).isNull();
assertThat(manager.getValue(textWithDefaultValue)).isEqualTo("bob");
}
@Test
public void givenNumberMetadataThenCanRetrieveAndAlterValue()
throws Exception {
assertThat(manager.getValue(number)).isNull();
assertThat(manager.getValue(numberWithDefaultValue)).isEqualTo(42);
manager.setValue(number, 12);
manager.setValue(numberWithDefaultValue, 34);
assertThat(manager.getValue(number)).isEqualTo(12);
assertThat(manager.getValue(numberWithDefaultValue)).isEqualTo(34);
manager.setValue(number, 56);
manager.setValue(numberWithDefaultValue, 78);
assertThat(manager.getValue(number)).isEqualTo(56);
assertThat(manager.getValue(numberWithDefaultValue)).isEqualTo(78);
manager.reset(number);
manager.reset(numberWithDefaultValue);
assertThat(manager.getValue(number)).isNull();
assertThat(manager.getValue(numberWithDefaultValue)).isEqualTo(42);
}
@Test
public void givenBinaryMetadataThenCanRetrieveAndAlterValue()
throws Exception {
assertThat(manager.getValue(binary)).isNull();
manager.setValue(binary, getTestResourceInputStreamFactory("binary1.png"));
StreamFactory<InputStream> value = manager.getValue(binary);
assertThat(value.create(SDK_STREAM)).hasContentEqualTo(getTestResourceInputStream("binary1.png"));
manager.setValue(binary, getTestResourceInputStreamFactory("binary2.png"));
value = manager.getValue(binary);
assertThat(value.create(SDK_STREAM)).hasContentEqualTo(getTestResourceInputStream("binary2.png"));
manager.reset(binary);
assertThat(manager.getValue(binary)).isNull();
manager.reset(binary);
assertThat(manager.getValue(binary)).isNull();
}
@Test
public void givenBooleanMetadataThenCanRetrieveAndAlterValue()
throws Exception {
assertThat(manager.getValue(booleanWithFalseByDefault)).isEqualTo(Boolean.FALSE);
assertThat(manager.getValue(booleanWithTrueByDefault)).isEqualTo(Boolean.TRUE);
manager.setValue(booleanWithFalseByDefault, true);
manager.setValue(booleanWithTrueByDefault, false);
assertThat(manager.getValue(booleanWithFalseByDefault)).isEqualTo(true);
assertThat(manager.getValue(booleanWithTrueByDefault)).isEqualTo(false);
manager.reset(booleanWithFalseByDefault);
manager.reset(booleanWithTrueByDefault);
assertThat(manager.getValue(booleanWithFalseByDefault)).isEqualTo(Boolean.FALSE);
assertThat(manager.getValue(booleanWithTrueByDefault)).isEqualTo(Boolean.TRUE);
}
@Test
public void givenEnumMetadataThenCanRetrieveAndAlterValue()
throws Exception {
assertThat(manager.getValue(enumValue)).isNull();
assertThat(manager.getValue(enumWithDefaultValue)).isEqualTo(AValidEnum.FIRST_VALUE);
manager.setValue(enumValue, AValidEnum.SECOND_VALUE);
manager.setValue(enumWithDefaultValue, AValidEnum.FIRST_VALUE);
assertThat(manager.getValue(enumValue)).isEqualTo(AValidEnum.SECOND_VALUE);
assertThat(manager.getValue(enumWithDefaultValue)).isEqualTo(AValidEnum.FIRST_VALUE);
manager.reset(enumValue);
manager.reset(enumWithDefaultValue);
assertThat(manager.getValue(enumValue)).isNull();
assertThat(manager.getValue(enumWithDefaultValue)).isEqualTo(AValidEnum.FIRST_VALUE);
}
@Test
public void givenCoreConfigUsedByMetadatasWhenChangedThenRecordsUpdated()
throws Exception {
assertThat(findUserByTitleInCollection(zeCollection, "Dakota L'Indien")).isNotNull();
assertThat(findUserByTitleInCollection(zeCollection, "L'Indien, Dakota")).isNull();
assertThat(findUserByTitleInCollection(aThirdCollection, "Dakota L'Indien")).isNotNull();
assertThat(findUserByTitleInCollection(aThirdCollection, "L'Indien, Dakota")).isNull();
manager.setValue(ConstellioEIMConfigs.USER_TITLE_PATTERN, "${lastName}, ${firstName}");
waitForBatchProcess();
assertThat(findUserByTitleInCollection(zeCollection, "Dakota L'Indien")).isNull();
assertThat(findUserByTitleInCollection(zeCollection, "L'Indien, Dakota")).isNotNull();
assertThat(findUserByTitleInCollection(aThirdCollection, "Dakota L'Indien")).isNull();
assertThat(findUserByTitleInCollection(aThirdCollection, "L'Indien, Dakota")).isNotNull();
}
@Test
public void givenModuleConfigUsedByACalculatorWhen()
throws Exception {
long numberOfUsers = 10L;
assertThat(countUsersWithFavoriteNumberInCollection(42.0, zeCollection)).isEqualTo(numberOfUsers);
assertThat(countUsersWithFavoriteNumberInCollection(42.0, anotherCollection)).isEqualTo(numberOfUsers);
assertThat(countUsersWithFavoriteNumberInCollection(666.0, zeCollection)).isEqualTo(0);
assertThat(countUsersWithFavoriteNumberInCollection(666.0, anotherCollection)).isEqualTo(0);
manager.setValue(numberUsedByCalculators, 666);
waitForBatchProcess();
assertThat(countUsersWithFavoriteNumberInCollection(42.0, zeCollection)).isEqualTo(0);
assertThat(countUsersWithFavoriteNumberInCollection(42.0, anotherCollection)).isEqualTo(0);
assertThat(countUsersWithFavoriteNumberInCollection(666.0, zeCollection)).isEqualTo(numberOfUsers);
assertThat(countUsersWithFavoriteNumberInCollection(666.0, anotherCollection)).isEqualTo(numberOfUsers);
}
@Test
public void givenModuleConfigEnablingModified()
throws Exception {
assertThat(getFirstnameMetadataIn(zeCollection).isUniqueValue()).isFalse();
assertThat(getFirstnameMetadataIn(anotherCollection).isUniqueValue()).isFalse();
assertThat(getFirstnameMetadataIn(aThirdCollection).isUniqueValue()).isFalse();
manager.setValue(textAlteringSchemas, "ohHellYeah");
assertThat(getFirstnameMetadataIn(zeCollection).isUniqueValue()).isTrue();
assertThat(getFirstnameMetadataIn(anotherCollection).isUniqueValue()).isTrue();
assertThat(getFirstnameMetadataIn(aThirdCollection).isUniqueValue()).isFalse();
assertThat(callCollectionsActionCallCount.get()).isEqualTo(1);
manager.reset(textAlteringSchemas);
assertThat(getFirstnameMetadataIn(zeCollection).isUniqueValue()).isFalse();
assertThat(getFirstnameMetadataIn(anotherCollection).isUniqueValue()).isFalse();
assertThat(getFirstnameMetadataIn(aThirdCollection).isUniqueValue()).isFalse();
assertThat(callCollectionsActionCallCount.get()).isEqualTo(2);
ValidationErrors errors = new ValidationErrors();
manager.validate(textAlteringSchemas, "invalidValue!", errors);
assertThat(errors.getValidationErrors()).hasSize(1);
assertThat(errors.getValidationErrors().get(0).getCode()).endsWith("ohBobo");
try {
manager.setValue(textAlteringSchemas, "invalidValue!");
fail("SystemConfigurationsManagerRuntimeException_InvalidConfigValue expected");
} catch (SystemConfigurationsManagerRuntimeException_InvalidConfigValue e) {
//OK
}
}
private long countUsersWithFavoriteNumberInCollection(double number, String collection) {
MetadataSchema userSchema = getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(collection)
.getSchema(User.DEFAULT_SCHEMA);
LogicalSearchCondition condition = LogicalSearchQueryOperators.from(userSchema).where(
userSchema.getMetadata("favoriteNumber")).isEqualTo(number);
return getModelLayerFactory().newSearchServices().getResultsCount(condition);
}
private Metadata getUsernameMetadataIn(String collection) {
MetadataSchema userSchema = getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(collection)
.getSchema(User.DEFAULT_SCHEMA);
return userSchema.getMetadata(User.USERNAME);
}
private Metadata getFirstnameMetadataIn(String collection) {
MetadataSchema userSchema = getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(collection)
.getSchema(User.DEFAULT_SCHEMA);
return userSchema.getMetadata(User.FIRSTNAME);
}
private Record findUserByTitleInCollection(String collection, String title) {
MetadataSchema userSchema = getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(collection)
.getSchema(User.DEFAULT_SCHEMA);
LogicalSearchCondition condition = LogicalSearchQueryOperators.from(userSchema).where(Schemas.TITLE).isEqualTo(title);
return getModelLayerFactory().newSearchServices().searchSingleResult(condition);
}
// ------------------------
public static class ZeModule implements InstallableModule {
@Override
public String getId() {
return "zeModule";
}
@Override
public String getName() {
return "zeModuleName";
}
@Override
public String getPublisher() {
return "sdk";
}
@Override
public List<MigrationScript> getMigrationScripts() {
List<MigrationScript> scripts = new ArrayList<>();
scripts.add(new MigrationScript() {
@Override
public String getVersion() {
return "5.0.1";
}
@Override
public void migrate(String collection, MigrationResourcesProvider migrationResourcesProvider,
AppLayerFactory appLayerFactory) {
ModelLayerFactory modelLayerFactory = appLayerFactory.getModelLayerFactory();
MetadataSchemaTypesBuilder typesBuilder = modelLayerFactory.getMetadataSchemasManager().modify(collection);
typesBuilder.getSchema(User.DEFAULT_SCHEMA).create("favoriteNumber").setType(MetadataValueType.NUMBER)
.defineDataEntry().asCalculated(FavoriteNumberCalculator.class);
try {
modelLayerFactory.getMetadataSchemasManager().saveUpdateSchemaTypes(typesBuilder);
} catch (OptimisticLocking optimistickLocking) {
throw new RuntimeException(optimistickLocking);
}
}
});
return scripts;
}
@Override
public void configureNavigation(NavigationConfig config) {
}
@Override
public boolean isComplementary() {
return false;
}
@Override
public List<String> getDependencies() {
return new ArrayList<>();
}
@Override
public List<SystemConfiguration> getConfigurations() {
return asList(text, textWithDefaultValue, booleanWithTrueByDefault, booleanWithFalseByDefault, number,
numberWithDefaultValue, enumValue, enumWithDefaultValue);
}
@Override
public Map<String, List<String>> getPermissions() {
return new HashMap<>();
}
@Override
public List<String> getRolesForCreator() {
return new ArrayList<>();
}
@Override
public void start(String collection, AppLayerFactory appLayerFactory) {
}
@Override
public void stop(String collection, AppLayerFactory appLayerFactory) {
}
@Override
public void addDemoData(String collection, AppLayerFactory appLayerFactory) {
}
}
public static class TextAlteringSchemasScript implements SystemConfigurationScript<String> {
@Override
public void validate(String newValue, ValidationErrors errors) {
if (!"ohHellYeah".equals(newValue) && !"ohHellNo".equals(newValue)) {
errors.add(TextAlteringSchemasScript.class, "ohBobo");
}
}
@Override
public void onValueChanged(String previousValue, String newValue, ModelLayerFactory modelLayerFactory) {
callCollectionsActionCallCount.incrementAndGet();
}
@Override
public void onValueChanged(String previousValue, String newValue, ModelLayerFactory modelLayerFactory,
String collection) {
MetadataSchemasManager schemasManager = modelLayerFactory.getMetadataSchemasManager();
MetadataSchemaTypesBuilder typesBuilder = schemasManager.modify(collection);
typesBuilder.getSchema(User.DEFAULT_SCHEMA).get(User.FIRSTNAME).setUniqueValue("ohHellYeah".equals(newValue));
try {
modelLayerFactory.getMetadataSchemasManager().saveUpdateSchemaTypes(typesBuilder);
} catch (OptimisticLocking optimistickLocking) {
throw new RuntimeException(optimistickLocking);
}
}
}
public static class FavoriteNumberCalculator implements MetadataValueCalculator<Double> {
ConfigDependency<Integer> numberConfig = new ConfigDependency<>(numberUsedByCalculators);
@Override
public Double calculate(CalculatorParameters parameters) {
Integer configValue = parameters.get(numberConfig);
return configValue.doubleValue();
}
@Override
public Double getDefaultValue() {
return 0.0;
}
@Override
public MetadataValueType getReturnType() {
return MetadataValueType.NUMBER;
}
@Override
public boolean isMultiValue() {
return false;
}
@Override
public List<? extends Dependency> getDependencies() {
return asList(numberConfig);
}
}
}