package org.molgenis.data.meta.system;
import org.molgenis.data.DataService;
import org.molgenis.data.RepositoryCollection;
import org.molgenis.data.meta.EntityTypeDependencyResolver;
import org.molgenis.data.meta.MetaDataService;
import org.molgenis.data.meta.SystemEntityType;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.meta.model.Package;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.StreamSupport.stream;
import static org.molgenis.data.meta.model.EntityTypeMetadata.ENTITY_TYPE_META_DATA;
import static org.molgenis.data.meta.model.PackageMetadata.PACKAGE;
import static org.molgenis.data.system.model.RootSystemPackage.PACKAGE_SYSTEM;
/**
* Persists {@link SystemEntityType} in the meta data {@link org.molgenis.data.RepositoryCollection}.
*/
@Component
public class SystemEntityTypePersister
{
private final DataService dataService;
private final SystemEntityTypeRegistry systemEntityTypeRegistry;
private final EntityTypeDependencyResolver entityTypeDependencyResolver;
@Autowired
public SystemEntityTypePersister(DataService dataService, SystemEntityTypeRegistry systemEntityTypeRegistry,
EntityTypeDependencyResolver entityTypeDependencyResolver)
{
this.dataService = requireNonNull(dataService);
this.systemEntityTypeRegistry = requireNonNull(systemEntityTypeRegistry);
this.entityTypeDependencyResolver = requireNonNull(entityTypeDependencyResolver);
}
public void persist(ContextRefreshedEvent event)
{
// persist entity meta data meta data
persistMetadataMetadata(event.getApplicationContext());
// persist entity meta data
List<EntityType> metaEntityMetaSet = systemEntityTypeRegistry.getSystemEntityTypes().collect(toList());
// inject attribute identifiers
injectExistingIdentifiers(metaEntityMetaSet);
// upsert entity types
dataService.getMeta().upsertEntityTypes(metaEntityMetaSet);
// remove entity meta data
removeNonExistingSystemEntities();
}
private void persistMetadataMetadata(ApplicationContext ctx)
{
MetaDataService metadataService = dataService.getMeta();
RepositoryCollection metadataRepoCollection = dataService.getMeta().getDefaultBackend();
// collect meta entity meta
List<EntityType> metaEntityTypeList = systemEntityTypeRegistry.getSystemEntityTypes()
.filter(metadataService::isMetaEntityType).collect(toList());
List<EntityType> resolvedEntityTypeList = entityTypeDependencyResolver.resolve(metaEntityTypeList);
resolvedEntityTypeList.forEach(metaEntityType ->
{
if (!metadataRepoCollection.hasRepository(metaEntityType))
{
metadataRepoCollection.createRepository(metaEntityType);
}
});
// collect meta entities
Map<String, Package> packageMap = ctx.getBeansOfType(Package.class);
List<Package> packagesToAdd = packageMap.values().stream().filter(this::isNotPersisted).collect(toList());
if (!packagesToAdd.isEmpty())
{
persist(packagesToAdd);
}
}
private boolean isNotPersisted(Package package_)
{
return dataService.findOneById(PACKAGE, package_.getIdValue(), Package.class) == null;
}
private void persist(List<Package> packages)
{
dataService.add(PACKAGE, packages.stream());
}
/**
* Package-private for testability
*/
void removeNonExistingSystemEntities()
{
// get all system entities
List<EntityType> systemEntityMetaSet = dataService.findAll(ENTITY_TYPE_META_DATA, EntityType.class)
.filter(SystemEntityTypePersister::isSystemEntity).collect(toList());
// determine removed system entities
List<EntityType> removedSystemEntityMetas = systemEntityMetaSet.stream().filter(this::isNotExists)
.collect(toList());
if (!removedSystemEntityMetas.isEmpty())
{
dataService.getMeta().deleteEntityType(removedSystemEntityMetas);
}
}
private static boolean isSystemEntity(EntityType entityType)
{
Package package_ = entityType.getPackage();
if (package_ == null)
{
return false;
}
if (package_.getName().equals(PACKAGE_SYSTEM))
{
return true;
}
Package rootPackage = package_.getRootPackage();
return rootPackage != null && rootPackage.getName().equals(PACKAGE_SYSTEM);
}
private boolean isNotExists(EntityType entityType)
{
return !systemEntityTypeRegistry.hasSystemEntityType(entityType.getName());
}
/**
* Inject existing attribute identifiers in system entity types
*
* @param entityTypes system entity types
*/
private void injectExistingIdentifiers(List<EntityType> entityTypes)
{
Map<Object, EntityType> existingEntityTypeMap = dataService
.findAll(ENTITY_TYPE_META_DATA, entityTypes.stream().map(EntityType::getIdValue), EntityType.class)
.collect(toMap(EntityType::getIdValue, Function.identity()));
entityTypes.forEach(entityType ->
{
EntityType existingEntityType = existingEntityTypeMap.get(entityType.getIdValue());
if (existingEntityType != null)
{
Map<String, Attribute> existingAttrs = stream(existingEntityType.getOwnAllAttributes().spliterator(),
false).collect(toMap(Attribute::getName, Function.identity()));
entityType.getOwnAllAttributes().forEach(attr ->
{
Attribute existingAttr = existingAttrs.get(attr.getName());
if (existingAttr != null)
{
// inject existing attribute identifier
attr.setIdentifier(existingAttr.getIdentifier());
}
});
}
});
}
}