package com.constellio.app.services.migrations.scripts;
import static com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails.END_DATE;
import static com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails.ROLES;
import static com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails.START_DATE;
import static com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails.SYNCED;
import static com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails.TARGET;
import static com.constellio.model.entities.schemas.MetadataValueType.BOOLEAN;
import static com.constellio.model.entities.schemas.MetadataValueType.DATE;
import static com.constellio.model.entities.schemas.MetadataValueType.STRING;
import static com.constellio.model.entities.schemas.Schemas.AUTHORIZATIONS;
import static com.constellio.model.entities.schemas.entries.DataEntryType.CALCULATED;
import static com.constellio.model.services.schemas.builders.CommonMetadataBuilder.TOKENS;
import static com.constellio.model.services.search.query.logical.LogicalSearchQuery.query;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasIn;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.where;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.app.entities.modules.MetadataSchemasAlterationHelper;
import com.constellio.app.entities.modules.MigrationResourcesProvider;
import com.constellio.app.entities.modules.MigrationScript;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.data.dao.dto.records.OptimisticLockingResolution;
import com.constellio.data.utils.BatchBuilderIterator;
import com.constellio.data.utils.KeySetMap;
import com.constellio.model.entities.records.ActionExecutorInBatch;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.RecordUpdateOptions;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.Facet;
import com.constellio.model.entities.records.wrappers.Group;
import com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.records.wrappers.structure.FacetType;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.entities.schemas.entries.CalculatedDataEntry;
import com.constellio.model.entities.schemas.entries.DataEntry;
import com.constellio.model.entities.security.XMLAuthorizationDetails;
import com.constellio.model.entities.security.global.AuthorizationDetails;
import com.constellio.model.services.records.RecordServicesException;
import com.constellio.model.services.records.SchemasRecordsServices;
import com.constellio.model.services.schemas.builders.MetadataSchemaBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypeBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypesBuilder;
import com.constellio.model.services.schemas.calculators.TokensCalculator2;
import com.constellio.model.services.schemas.calculators.TokensCalculator3;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.security.AuthorizationDetailsManager;
public class CoreMigrationTo_7_0 implements MigrationScript {
private static final Logger LOGGER = LoggerFactory.getLogger(CoreMigrationTo_7_0.class);
@Override
public String getVersion() {
return "7.0";
}
@Override
public void migrate(String collection, MigrationResourcesProvider migrationResourcesProvider, AppLayerFactory appLayerFactory)
throws Exception {
removeQueryFacet(collection, appLayerFactory);
new CoreSchemaAlterationFor7_0(collection, migrationResourcesProvider, appLayerFactory).migrate();
convertXMLAuthorizationDetailsToSolrAuthorizationDetails(collection, appLayerFactory);
appLayerFactory.getSystemGlobalConfigsManager().setReindexingRequired(true);
}
private void removeQueryFacet(String collection, AppLayerFactory appLayerFactory)
throws Exception {
SchemasRecordsServices schemas = new SchemasRecordsServices(collection, appLayerFactory.getModelLayerFactory());
SearchServices searchServices = appLayerFactory.getModelLayerFactory().newSearchServices();
List<Facet> facetList = schemas.wrapFacets(searchServices.search(query(from(schemas.facetSchemaType()).whereAllConditions(
where(schemas.facetQuerySchema().getMetadata(Facet.FACET_TYPE)).isEqualTo(FacetType.QUERY),
where(schemas.facetQuerySchema().getMetadata(Facet.TITLE)).isEqualTo("Création/Modification date")
))));
Transaction transaction = new Transaction();
for (Facet facet : facetList) {
transaction.update(facet.setActive(false).getWrappedRecord());
}
appLayerFactory.getModelLayerFactory().newRecordServices().execute(transaction);
}
private void convertXMLAuthorizationDetailsToSolrAuthorizationDetails(String collection, AppLayerFactory appLayerFactory)
throws RecordServicesException {
AuthorizationDetailsManager manager = appLayerFactory.getModelLayerFactory().getAuthorizationDetailsManager();
Map<String, AuthorizationDetails> xmlAuthorizationDetailsList = manager.getAuthorizationsDetails(collection);
SchemasRecordsServices schemasRecordsServices = new SchemasRecordsServices(collection,
appLayerFactory.getModelLayerFactory());
SearchServices searchServices = appLayerFactory.getModelLayerFactory().newSearchServices();
Set<String> convertedAuthDetails = new HashSet<>();
try {
KeySetMap<String, String> authsTargets = getAuthsTargets(collection, appLayerFactory);
List<AuthToConvert> authToConverts = new ArrayList<>();
for (AuthorizationDetails details : xmlAuthorizationDetailsList.values()) {
convertedAuthDetails.add(details.getId());
AuthToConvert authToConvert = new AuthToConvert();
authToConvert.details = (XMLAuthorizationDetails) details;
authToConvert.targets = authsTargets.get(details.getId());
authToConverts.add(authToConvert);
}
for (Map.Entry<String, Set<String>> authTarget : authsTargets.getNestedMap().entrySet()) {
if (!convertedAuthDetails.contains(authTarget.getKey())) {
LOGGER.warn("No such authorization detail for auth id '" + authTarget.getKey() + "' on targets '" + authTarget
.getValue() + "'");
}
}
Iterator<List<AuthToConvert>> iterator = new BatchBuilderIterator<>(authToConverts.iterator(), 1000);
KeySetMap<String, String> authCopies = buildSolrAuthorizationDetails(iterator, schemasRecordsServices,
appLayerFactory);
convertUsersAndGroupAuths(appLayerFactory, schemasRecordsServices, authCopies, convertedAuthDetails);
} catch (Exception e) {
throw new RuntimeException("Migration failed", e);
}
}
private KeySetMap<String, String> getAuthsTargets(String collection, final AppLayerFactory appLayerFactory)
throws Exception {
final KeySetMap<String, String> authsTargets = new KeySetMap<>();
final List<String> restrictedSchemaTypes = asList(User.SCHEMA_TYPE, Group.SCHEMA_TYPE);
new ActionExecutorInBatch(appLayerFactory.getModelLayerFactory().newSearchServices(), "Find auth targets", 1000) {
@Override
public void doActionOnBatch(List<Record> records)
throws Exception {
Transaction transaction = new Transaction();
transaction.setOptions(RecordUpdateOptions.validationExceptionSafeOptions());
transaction.getRecordUpdateOptions().setOptimisticLockingResolution(OptimisticLockingResolution.EXCEPTION);
for (Record record : records) {
if (!restrictedSchemaTypes.contains(record.getTypeCode())) {
for (String auth : record.<String>getList(Schemas.AUTHORIZATIONS)) {
authsTargets.add(auth, record.getId());
}
record.set(Schemas.AUTHORIZATIONS, new ArrayList<>());
transaction.add(record);
}
}
appLayerFactory.getModelLayerFactory().newRecordServices().executeWithoutImpactHandling(transaction);
}
}.execute(fromAllSchemasIn(collection).where(Schemas.AUTHORIZATIONS).isNotNull());
return authsTargets;
}
private void convertUsersAndGroupAuths(final AppLayerFactory appLayerFactory,
final SchemasRecordsServices schemasRecordsServices, final KeySetMap<String, String> authCopies,
final Set<String> convertedAuthDetails)
throws Exception {
final Set<String> oldAuths = new HashSet<>();
new ActionExecutorInBatch(appLayerFactory.getModelLayerFactory().newSearchServices(), "Convert users/group auths", 1000) {
@Override
public void doActionOnBatch(List<Record> records)
throws Exception {
Transaction transaction = new Transaction();
transaction.setOptions(RecordUpdateOptions.validationExceptionSafeOptions());
transaction.getRecordUpdateOptions().setOptimisticLockingResolution(OptimisticLockingResolution.EXCEPTION);
for (Record record : records) {
oldAuths.addAll(convertAuthsOf(record, authCopies, schemasRecordsServices, convertedAuthDetails));
transaction.add(record);
}
appLayerFactory.getModelLayerFactory().newRecordServices().executeWithoutImpactHandling(transaction);
}
}.execute(from(schemasRecordsServices.group.schemaType(), schemasRecordsServices.user.schemaType()).returnAll());
}
private List<String> convertAuthsOf(Record record, KeySetMap<String, String> authCopies, SchemasRecordsServices schemas,
Set<String> convertedAuthDetails) {
List<String> oldAuths = record.getList(Schemas.AUTHORIZATIONS);
List<String> newAuths = new ArrayList<>();
for (String oldAuth : oldAuths) {
Set<String> newCopies = authCopies.get(oldAuth);
newAuths.addAll(newCopies);
if (!convertedAuthDetails.contains(oldAuth)) {
String code = record.getTypeCode().equals(User.SCHEMA_TYPE) ? record.<String>get(schemas.user.username()) :
("group " + record.get(schemas.group.code()));
LOGGER.warn("No such authorization detail for auth id '" + oldAuth + "' on principal '" + code + "'");
}
}
record.set(Schemas.AUTHORIZATIONS, newAuths);
return oldAuths;
}
private List<String> findTargets(SearchServices searchServices, SchemasRecordsServices schemas,
AuthorizationDetails details) {
return searchServices.searchRecordIds(
fromAllSchemasIn(details.getCollection()).where(AUTHORIZATIONS).isEqualTo(details.getId()));
}
private static class AuthToConvert {
XMLAuthorizationDetails details;
Set<String> targets;
}
private KeySetMap<String, String> buildSolrAuthorizationDetails(Iterator<List<AuthToConvert>> authsToConvertIterator,
SchemasRecordsServices schemasRecordsServices, AppLayerFactory appLayerFactory)
throws RecordServicesException {
KeySetMap<String, String> authCopies = new KeySetMap<>();
while (authsToConvertIterator.hasNext()) {
List<AuthToConvert> authsToConvert = authsToConvertIterator.next();
Transaction transaction = new Transaction();
transaction.getRecordUpdateOptions().setOptimisticLockingResolution(OptimisticLockingResolution.EXCEPTION);
for (AuthToConvert authToConvert : authsToConvert) {
for (String target : authToConvert.targets) {
SolrAuthorizationDetails solrAuthorizationDetails = schemasRecordsServices
.newSolrAuthorizationDetails();
if (authToConvert.details.getStartDate() == null
|| authToConvert.details.getStartDate().getYear() < 2007) {
solrAuthorizationDetails.setStartDate(null);
} else {
solrAuthorizationDetails.setStartDate(authToConvert.details.getStartDate());
}
if (authToConvert.details.getEndDate() == null || authToConvert.details.getEndDate().getYear() < 2007) {
solrAuthorizationDetails.setEndDate(null);
} else {
solrAuthorizationDetails.setEndDate(authToConvert.details.getEndDate());
}
if (!authToConvert.details.isSynced()) {
solrAuthorizationDetails.setSynced(null);
} else {
solrAuthorizationDetails.setSynced(true);
}
solrAuthorizationDetails.setRoles(authToConvert.details.getRoles());
solrAuthorizationDetails.setTarget(target);
transaction.add(solrAuthorizationDetails);
authCopies.add(authToConvert.details.getId(), solrAuthorizationDetails.getId());
}
}
appLayerFactory.getModelLayerFactory().newRecordServices().execute(transaction);
}
return authCopies;
}
private class CoreSchemaAlterationFor7_0 extends MetadataSchemasAlterationHelper {
public CoreSchemaAlterationFor7_0(String collection, MigrationResourcesProvider migrationResourcesProvider,
AppLayerFactory appLayerFactory) {
super(collection, migrationResourcesProvider, appLayerFactory);
}
@Override
protected void migrate(MetadataSchemaTypesBuilder typesBuilder) {
createReportSchemaType(typesBuilder);
setNewTokenCalculator(typesBuilder);
}
private void setNewTokenCalculator(MetadataSchemaTypesBuilder typesBuilder) {
for (MetadataSchemaTypeBuilder typeBuilder : typesBuilder.getTypes()) {
DataEntry dataEntry = typeBuilder.getDefaultSchema().get(TOKENS).getDataEntry();
// if (dataEntry.getType() == CALCULATED
// && ((CalculatedDataEntry) dataEntry).getCalculator() instanceof TokensCalculator2) {
// typeBuilder.getDefaultSchema().get(TOKENS).defineDataEntry().asCalculated(TokensCalculator3.class);
// } else {
}
}
private MetadataSchemaTypeBuilder createReportSchemaType(MetadataSchemaTypesBuilder typesBuilder) {
MetadataSchemaTypeBuilder type = typesBuilder.createNewSchemaType(SolrAuthorizationDetails.SCHEMA_TYPE);
MetadataSchemaBuilder defaultSchema = type.getDefaultSchema();
defaultSchema.createUndeletable(ROLES).setType(STRING).setMultivalue(true).setDefaultRequirement(true);
defaultSchema.createUndeletable(SYNCED).setType(BOOLEAN);
defaultSchema.createUndeletable(START_DATE).setType(DATE);
defaultSchema.createUndeletable(END_DATE).setType(DATE);
defaultSchema.createUndeletable(TARGET).setType(STRING).setDefaultRequirement(true);
return type;
}
}
}