diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java index fc74d40faea5af8282aceb19c1128b412cb77a53..7a04ddbd043cca7341a0dd4df3a55abfa9f166af 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -41,9 +42,11 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDeletionDAO; import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleDAO; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetFetchOption; +import ch.systemsx.cisd.openbis.generic.shared.basic.IIdAndCodeHolder; import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder; import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentifierHolder; import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.AbstractExternalData; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Code; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetArchivingStatus; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; @@ -57,9 +60,11 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind; import ch.systemsx.cisd.openbis.generic.shared.managed_property.IManagedPropertyEvaluatorFactory; +import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils; /** * @author Piotr Buczek + * @author Franz-Josef Elmer */ public class TrashBO extends AbstractBusinessObject implements ITrashBO { @@ -108,7 +113,48 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO @Override public void trashDataSets(List<TechId> dataSetIds) { - trashDataSets(dataSetIds, true, null); + assert deletion != null; + + IDatasetLister datasetLister = boFactory.createDatasetLister(session); + final Set<TechId> experimentIds = new HashSet<TechId>(); + final Set<TechId> sampleIds = new HashSet<TechId>(); + for (AbstractExternalData dataSet : datasetLister.listByDatasetIds(TechId.asLongs(dataSetIds), DATA_SET_FETCH_OPTIONS)) + { + Sample sample = dataSet.getSample(); + if (sample != null) + { + sampleIds.add(new TechId(sample)); + } else if (dataSet.getExperiment() != null) + { + experimentIds.add(new TechId(dataSet.getExperiment())); + } + } + assertDataSetDeletionBusinessRules(experimentIds, sampleIds, dataSetIds); + TrashOperationsManager trashManager = new TrashOperationsManager(deletion, getDeletionDAO()); + trashDataSets(trashManager, dataSetIds, true, new IDataSetFilter() + { + @Override + public List<TechId> filter(List<DataPE> dataSets) + { + List<TechId> filtered = new ArrayList<TechId>(); + for (DataPE dataSet : dataSets) + { + SamplePE sample = dataSet.tryGetSample(); + ExperimentPE experiment = dataSet.getExperiment(); + if (contains(sampleIds, sample) || contains(experimentIds, experiment)) + { + filtered.add(new TechId(dataSet)); + } + } + return filtered; + } + + private boolean contains(Set<TechId> ids, IIdAndCodeHolder entity) + { + return entity != null && ids.contains(new TechId(HibernateUtils.getId(entity))); + } + }); + trashManager.trash(); } @Override @@ -116,18 +162,13 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO { assert deletion != null; - TrashBatchOperation batchOperation = - new TrashBatchOperation(EntityKind.EXPERIMENT, experimentIds, deletion, - getDeletionDAO(), true); - BatchOperationExecutor.executeInBatches(batchOperation); - - if (batchOperation.counter > 0) - { - Set<TechId> eIds = new LinkedHashSet<TechId>(experimentIds); - Set<TechId> dependentSampleIds = trashExperimentDependentSamples(eIds); - assertSampleDeletionBusinessRules(eIds, dependentSampleIds); - trashExperimentDependentDataSets(eIds, dependentSampleIds); - } + TrashOperationsManager trashManager = new TrashOperationsManager(deletion, getDeletionDAO()); + trashManager.addTrashOperation(EntityKind.EXPERIMENT, experimentIds, true); + Set<TechId> eIds = new LinkedHashSet<TechId>(experimentIds); + Set<TechId> dependentSampleIds = trashExperimentDependentSamples(trashManager, eIds); + assertSampleDeletionBusinessRules(eIds, dependentSampleIds); + trashExperimentDependentDataSets(trashManager, eIds, dependentSampleIds); + trashManager.trash(); } @Override @@ -135,31 +176,36 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO { assert deletion != null; - Set<TechId> experimentIds = Collections.<TechId>emptySet(); - Set<TechId> allSampleIds = trashSamples(experimentIds, sampleIds, - CascadeSampleDependentComponents.TRUE, true); + TrashOperationsManager trashManager = new TrashOperationsManager(deletion, getDeletionDAO()); + Set<TechId> experimentIds = new HashSet<TechId>(); + ISampleLister sampleLister = boFactory.createSampleLister(session); + List<Sample> samples = sampleLister.list(new ListOrSearchSampleCriteria(TechId.asLongs(sampleIds))); + for (Sample sample : samples) + { + Experiment experiment = sample.getExperiment(); + if (experiment != null) + { + experimentIds.add(new TechId(experiment)); + } + } + Set<TechId> allSampleIds = trashSamples(trashManager, experimentIds, + sampleIds, CascadeSampleDependentComponents.TRUE, true); assertSampleDeletionBusinessRules(experimentIds, allSampleIds); - trashSampleDependentDataSets(allSampleIds); - + trashSampleDependentDataSets(trashManager, allSampleIds); + trashManager.trash(); } - private Set<TechId> trashSamples(Set<TechId> experimentIds, final List<TechId> sampleIds, - final CascadeSampleDependentComponents cascadeType, boolean isOriginalDeletion) + private Set<TechId> trashSamples(TrashOperationsManager trashManager, Set<TechId> experimentIds, + final List<TechId> sampleIds, final CascadeSampleDependentComponents cascadeType, boolean isOriginalDeletion) { assert deletion != null; Set<TechId> allSampleIds = new LinkedHashSet<TechId>(sampleIds); - TrashBatchOperation batchOperation = - new TrashBatchOperation(EntityKind.SAMPLE, sampleIds, deletion, getDeletionDAO(), - isOriginalDeletion); - BatchOperationExecutor.executeInBatches(batchOperation); + trashManager.addTrashOperation(EntityKind.SAMPLE, sampleIds, isOriginalDeletion); - if (batchOperation.counter > 0) + if (cascadeType == CascadeSampleDependentComponents.TRUE) { - if (cascadeType == CascadeSampleDependentComponents.TRUE) - { - allSampleIds.addAll(trashSampleDependentComponents(experimentIds, sampleIds)); - } + allSampleIds.addAll(trashSampleDependentComponents(trashManager, experimentIds, sampleIds)); } return allSampleIds; } @@ -200,7 +246,8 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO } } - private void trashDataSets(final List<TechId> dataSetIds, boolean isOriginalDeletion, IDataSetFilter filterOrNull) + private void trashDataSets(TrashOperationsManager trashManager, final List<TechId> dataSetIds, + boolean isOriginalDeletion, IDataSetFilter filterOrNull) { assert deletion != null; @@ -223,16 +270,8 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO if (isOriginalDeletion) { allDeletables.removeAll(deletableOriginals); - - TrashBatchOperation batchOperation = - new TrashBatchOperation(EntityKind.DATA_SET, deletableOriginals, deletion, - getDeletionDAO(), true); - BatchOperationExecutor.executeInBatches(batchOperation); - - batchOperation = - new TrashBatchOperation(EntityKind.DATA_SET, allDeletables, deletion, - getDeletionDAO(), false); - BatchOperationExecutor.executeInBatches(batchOperation); + trashManager.addTrashOperation(EntityKind.DATA_SET, deletableOriginals, true); + trashManager.addTrashOperation(EntityKind.DATA_SET, allDeletables, false); } else { int nonDeletable = dataSetIds.size() - deletableOriginals.size(); @@ -244,10 +283,7 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO + Code.extractCodes(datasetLister.listByDatasetIds(TechId.asLongs(dataSetIds), EnumSet.of(DataSetFetchOption.BASIC)))); } - TrashBatchOperation batchOperation = - new TrashBatchOperation(EntityKind.DATA_SET, allDeletables, deletion, - getDeletionDAO(), false); - BatchOperationExecutor.executeInBatches(batchOperation); + trashManager.addTrashOperation(EntityKind.DATA_SET, allDeletables, false); } } @@ -283,7 +319,8 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO + builder); } - private Set<TechId> trashSampleDependentComponents(Set<TechId> experimentIds, List<TechId> sampleIds) + private Set<TechId> trashSampleDependentComponents(TrashOperationsManager trashManager, + Set<TechId> experimentIds, List<TechId> sampleIds) { final ISampleDAO sampleDAO = getSampleDAO(); @@ -300,10 +337,10 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO BatchOperationExecutor.executeInBatches(batchOperation); // We have a business rule that there is just 1 level of components and using this here // improves performance. - return trashSamples(experimentIds, batchOperation.getResults(), CascadeSampleDependentComponents.FALSE, false); + return trashSamples(trashManager, experimentIds, batchOperation.getResults(), CascadeSampleDependentComponents.FALSE, false); } - private void trashSampleDependentDataSets(Set<TechId> sampleIds) + private void trashSampleDependentDataSets(TrashOperationsManager trashManager, Set<TechId> sampleIds) { AbstractQueryBatchOperation batchOperation = new AbstractQueryBatchOperation(EntityKind.DATA_SET, new ArrayList<TechId>(sampleIds), @@ -319,18 +356,18 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO BatchOperationExecutor.executeInBatches(batchOperation); List<TechId> dataSetIds = batchOperation.getResults(); assertDataSetDeletionBusinessRules(Collections.<TechId>emptySet(), sampleIds, dataSetIds); - trashDataSets(dataSetIds, false, new DataSetFilter(sampleIds, - new IIdHolderProvider() - { - @Override - public IIdHolder getIdHolder(DataPE dataSet) - { - return dataSet.tryGetSample(); - } - })); + trashDataSets(trashManager, dataSetIds, false, new DataSetFilter(sampleIds, + new IIdHolderProvider() + { + @Override + public IIdHolder getIdHolder(DataPE dataSet) + { + return dataSet.tryGetSample(); + } + })); } - private Set<TechId> trashExperimentDependentSamples(Set<TechId> experimentIds) + private Set<TechId> trashExperimentDependentSamples(TrashOperationsManager trashManager, Set<TechId> experimentIds) { AbstractQueryBatchOperation batchOperation = new AbstractQueryBatchOperation(EntityKind.SAMPLE, new ArrayList<TechId>(experimentIds), @@ -344,10 +381,11 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO }; BatchOperationExecutor.executeInBatches(batchOperation); List<TechId> sampleIds = batchOperation.getResults(); - return trashSamples(experimentIds, sampleIds, CascadeSampleDependentComponents.TRUE, false); + return trashSamples(trashManager, experimentIds, sampleIds, CascadeSampleDependentComponents.TRUE, false); } - private void trashExperimentDependentDataSets(Set<TechId> experimentIds, Set<TechId> dependentSampleIds) + private void trashExperimentDependentDataSets(TrashOperationsManager trashManager, Set<TechId> experimentIds, + Set<TechId> dependentSampleIds) { AbstractQueryBatchOperation batchOperation = new AbstractQueryBatchOperation(EntityKind.DATA_SET, new ArrayList<TechId>(experimentIds), @@ -362,15 +400,15 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO BatchOperationExecutor.executeInBatches(batchOperation); List<TechId> dataSetIds = batchOperation.getResults(); assertDataSetDeletionBusinessRules(experimentIds, dependentSampleIds, dataSetIds); - trashDataSets(dataSetIds, false, new DataSetFilter(experimentIds, - new IIdHolderProvider() - { - @Override - public IIdHolder getIdHolder(DataPE dataSet) - { - return dataSet.getExperiment(); - } - })); + trashDataSets(trashManager, dataSetIds, false, new DataSetFilter(experimentIds, + new IIdHolderProvider() + { + @Override + public IIdHolder getIdHolder(DataPE dataSet) + { + return dataSet.getExperiment(); + } + })); } private void assertSampleDeletionBusinessRules(Set<TechId> experimentIds, Set<TechId> sampleIds) @@ -420,8 +458,6 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO private void assertDataSetDeletionBusinessRules(Set<TechId> experimentIds, Set<TechId> sampleIdes, List<TechId> dataSetIds) { - Set<Long> eIds = new LinkedHashSet<Long>(TechId.asLongs(experimentIds)); - Set<Long> sIds = new LinkedHashSet<Long>(TechId.asLongs(sampleIdes)); IDatasetLister datasetLister = boFactory.createDatasetLister(session); Map<Long, Set<Long>> containerIds = datasetLister.listContainerIds(TechId.asLongs(dataSetIds)); if (containerIds.isEmpty()) @@ -430,22 +466,25 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO } Set<Long> allRelatedDataSets = new LinkedHashSet<Long>(); addIds(allRelatedDataSets, containerIds); - IDataSetTable dataSetTable = boFactory.createDataSetTable(session); - dataSetTable.loadByIds(TechId.createList(new ArrayList<Long>(allRelatedDataSets))); + Set<Long> eIds = new LinkedHashSet<Long>(TechId.asLongs(experimentIds)); + Set<Long> sIds = new LinkedHashSet<Long>(TechId.asLongs(sampleIdes)); + List<AbstractExternalData> relatedDataSets + = datasetLister.listByDatasetIds(new ArrayList<Long>(allRelatedDataSets), DATA_SET_FETCH_OPTIONS); StringBuilder builder = new StringBuilder(); int numberOfForeignDataSets = 0; - for (DataPE relatedDataSet : dataSetTable.getDataSets()) + for (AbstractExternalData relatedDataSet : relatedDataSets) { if (numberOfForeignDataSets >= 10) { break; } - SamplePE sample = relatedDataSet.tryGetSample(); - ExperimentPE experiment = relatedDataSet.getExperiment(); + Sample sample = relatedDataSet.getSample(); + Experiment experiment = relatedDataSet.getExperiment(); if (sample != null) { if (sIds.contains(sample.getId()) == false) { + HibernateUtils.initialize(sample); addTo(builder, "sample " + sample.getIdentifier(), relatedDataSet, containerIds); numberOfForeignDataSets++; } @@ -453,6 +492,7 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO { if (eIds.contains(experiment.getId()) == false) { + HibernateUtils.initialize(experiment); addTo(builder, "experiment " + experiment.getIdentifier(), relatedDataSet, containerIds); numberOfForeignDataSets++; } @@ -464,7 +504,7 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO } } - private void addTo(StringBuilder builder, String entityDescription, DataPE dataSet, + private void addTo(StringBuilder builder, String entityDescription, AbstractExternalData dataSet, Map<Long, Set<Long>> containerIds) { String findOriginalDataSet = findOriginalDataSet(containerIds, dataSet); @@ -472,7 +512,7 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO + dataSet.getCode() + " which belongs to " + entityDescription + " outside the deletion set.\n"); } - private String findOriginalDataSet(Map<Long, Set<Long>> relations, DataPE dataSet) + private String findOriginalDataSet(Map<Long, Set<Long>> relations, AbstractExternalData dataSet) { Set<Entry<Long, Set<Long>>> entrySet = relations.entrySet(); for (Entry<Long, Set<Long>> entry : entrySet) @@ -505,6 +545,35 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO throwException(ex, "Deletion"); } } + + private static final class TrashOperationsManager + { + private final DeletionPE deletion; + private final IDeletionDAO deletionDAO; + private final List<TrashBatchOperation> operations = new ArrayList<TrashBatchOperation>(); + + TrashOperationsManager(DeletionPE deletion, IDeletionDAO deletionDAO) + { + this.deletion = deletion; + this.deletionDAO = deletionDAO; + } + + void addTrashOperation(EntityKind entityKind, List<TechId> entityIds, boolean isOriginalDeletion) + { + if (entityIds.isEmpty() == false) + { + operations.add(new TrashBatchOperation(entityKind, entityIds, deletion, deletionDAO, isOriginalDeletion)); + } + } + + void trash() + { + for (TrashBatchOperation operation : operations) + { + BatchOperationExecutor.executeInBatches(operation); + } + } + } private static class TrashBatchOperation implements IBatchOperation<TechId> { @@ -518,8 +587,6 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO private final boolean isOriginalDeletion; - private int counter = 0; - public TrashBatchOperation(EntityKind entityKind, List<TechId> entityIds, DeletionPE deletion, IDeletionDAO deletionDAO, boolean isOriginalDeletion) { @@ -533,7 +600,7 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO @Override public void execute(List<TechId> entities) { - counter += deletionDAO.trash(entityKind, entities, deletion, isOriginalDeletion); + deletionDAO.trash(entityKind, entities, deletion, isOriginalDeletion); } @Override diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBOTest.java index 45dee2414180e0c81483a66864dbb56db4f7ebd4..d53831d60094261e18918ff90e04e7dd1a2eb7ff 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBOTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBOTest.java @@ -41,8 +41,6 @@ import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool; import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.DataSetNode; import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.EntityGraphGenerator; -import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.EntityNode; -import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.ExperimentNode; import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.SampleNode; import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.Utils; import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant; @@ -194,6 +192,9 @@ public final class TrashBOTest extends AbstractBOTest @Test public final void testTrashExperimentsAlreadyTrashed() { + EntityGraphGenerator g = new EntityGraphGenerator(); + g.parse("E1\nE2\nE3\n"); + prepareEntityGraph(g); final DeletionPE deletion = createDeletion(); final List<TechId> experimentIds = EXAMPLE_ID_LIST; context.checking(new Expectations() @@ -207,404 +208,6 @@ public final class TrashBOTest extends AbstractBOTest context.assertIsSatisfied(); } - @Test - public final void testTrashExperimentsWithOneLevelOfDependencies() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S50, data sets: DS60 DS61\n" - + "E2, samples: S51\n" - + "E3\n" - ); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, g.e(1), g.e(2), g.e(3)); - prepareTrashSamples(deletion, false, g.s(50), g.s(51)); - prepareTrashDataSets(deletion, false, g.ds(60), g.ds(61)); - - trashBO.trashExperiments(asIds(g.e(1), g.e(2), g.e(3))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashExperimentsWithDeepDependencies() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S50, data sets: DS60 DS61\n" - + "E2, samples: S51\n" - + "E3\n" - + "S50, components: S52, data sets: DS70 DS71\n" - + "S51, components: S53\n" - ); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, g.e(1), g.e(2), g.e(3)); - prepareTrashSamples(deletion, false, g.s(50), g.s(51)); - prepareTrashSamples(deletion, false, g.s(52), g.s(53)); - prepareTrashDataSets(deletion, false, g.ds(60), g.ds(61), g.ds(70), g.ds(71)); - - trashBO.trashExperiments(asIds(g.e(1), g.e(2), g.e(3))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashExperimentWithSamplesAndDataSetsAndNoExternalLinks() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S1, data sets: DS1\n" - + "E2, data sets: DS2\n" - + "S1, data sets: DS1\n" - + "DS1, components: DS2\n"); - ExperimentNode e1 = g.e(1); - SampleNode s1 = g.s(1); - ExperimentNode e2 = g.e(2); - DataSetNode ds1 = g.ds(1); - DataSetNode ds2 = g.ds(2); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, e1, e2); - prepareTrashSamples(deletion, false, s1); - prepareTrashDataSets(deletion, false, ds1, ds2); - - trashBO.trashExperiments(asIds(e1, e2)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashExperimentWithARelatedDataSetComponentWhichBelongsToAnExternalExperiment() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S1, data sets: DS1\n" - + "E2, data sets: DS2\n" - + "S1, data sets: DS1\n" - + "DS1, components: DS2\n"); - ExperimentNode e1 = g.e(1); - SampleNode s1 = g.s(1); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, e1); - prepareTrashSamples(deletion, false, s1); - prepareTrashDataSets(deletion, false, g.ds(1)); - - trashBO.trashExperiments(asIds(e1)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashExperimentWithARelatedDataSetInAnExternalContainer() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S1, data sets: DS1\n" - + "E2, data sets: DS2\n" - + "S1, data sets: DS1\n" - + "DS1, components: DS2\n"); - ExperimentNode e2 = g.e(2); - DataSetNode ds2 = g.ds(2); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, e2); - - failTrashExperiment(e2, ds2, g.ds(1), g.s(1)); - - context.assertIsSatisfied(); - } - - @Test - public void testTrashPublishedExperiment() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, data sets: DS1 DS2 DS3 DS4\n" - + "DS1, components: DS3\n" - + "DS2, children: DS4\n" - + "E2, data sets: DS5 DS6\n" - + "DS5, components: DS1\n" - + "DS6, components: DS2\n"); - ExperimentNode e2 = g.e(2); // published experiment - DataSetNode ds5 = g.ds(5); - DataSetNode ds6 = g.ds(6); - prepareEntityGraph(g); - DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, e2); - prepareTrashDataSets(deletion, false, ds5 , ds6); - - trashBO.trashExperiments(asIds(e2)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashPublishedExperimentWithOrginalExperimentWithSamples() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S1\n" - + "S1, data sets: DS1 DS2\n" - + "DS1, components: DS2\n" - + "E2, data sets: DS3\n" - + "DS3, components: DS1"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, g.e(2)); - prepareTrashDataSets(deletion, false, g.ds(3)); - - trashBO.trashExperiments(asIds(g.e(2))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashOrginalExperimentWithSample() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S1\n" - + "S1, data sets: DS1 DS2\n" - + "DS1, components: DS2\n" - + "E2, data sets: DS3\n" - + "DS3, components: DS1"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, g.e(1)); - prepareTrashSamples(deletion, false, g.s(1)); - - failTrashExperiment(g.e(1), g.ds(1), g.ds(3), g.e(2)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashExperimentsWithContainerDataSetWithPhysicalDataSetFromAnotherExperiment() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, data sets: DS1\n" - + "E2, data sets: DS2\n" - + "DS1, components: DS2\n" - + "DS2, containers: DS1"); - prepareEntityGraph(g); - DeletionPE deletion = createDeletion(); - prepareTrashExperiments(deletion, true, g.e(1)); - prepareTrashDataSets(deletion, false, g.ds(1)); - - trashBO.trashExperiments(asIds(g.e(1))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSamplesAlreadyTrashed() - { - final EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1\nS2\n"); - prepareEntityGraph(g); - - final DeletionPE deletion = createDeletion(); - context.checking(new Expectations() - { - { - one(deletionDAO).trash(EntityKind.SAMPLE, asIds(g.s(1), g.s(2)), deletion, true); - will(returnValue(0)); - } - }); - trashBO.trashSamples(asIds(g.s(1), g.s(2))); - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSamplesWithOneLevelOfDependencies() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, components: S20\n" - + "S3, data sets: DS60\n" - + "S20, data sets: DS61\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1), g.s(3)); - prepareTrashSamples(deletion, false, g.s(20)); - prepareTrashDataSets(deletion, false, g.ds(60), g.ds(61)); - - trashBO.trashSamples(asIds(g.s(1), g.s(3))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSampleWithTwoLevelOfDependencies() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, components: S2, data sets: DS1\n" - + "S2, components: S3, data sets: DS2\n" - + "S3, data sets: DS3\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - prepareTrashSamples(deletion, false, g.s(2)); - prepareTrashDataSets(deletion, false, g.ds(1), g.ds(2)); - - trashBO.trashSamples(asIds(g.s(1))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSampleWithAComponentWithAContainerDataSetWithAComponentDataSetOfFirstSample() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, components: S2, data sets: DS1\n" - + "S2, data sets: DS2\n" - + "DS2, components: DS1\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - prepareTrashSamples(deletion, false, g.s(2)); - prepareTrashDataSets(deletion, false, g.ds(1), g.ds(2)); - - trashBO.trashSamples(asIds(g.s(1))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSampleWithAComponentWithAComponentDataSetOfAContainerDataSetOfFirstSample() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, components: S2, data sets: DS1\n" - + "S2, data sets: DS2\n" - + "DS1, components: DS2\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - prepareTrashSamples(deletion, false, g.s(2)); - prepareTrashDataSets(deletion, false, g.ds(1), g.ds(2)); - - trashBO.trashSamples(asIds(g.s(1))); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSampleWithAnExperimentSampleWithADataSet() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, samples: S2\n" - + "S1, components: S2\n" - + "S2, data sets: DS1\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - prepareTrashSamples(deletion, false, g.s(2)); - - failTrashSample(g.s(1), g.s(2), g.e(1)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSampleWithComponentWithDataSetWithComponentOfAnotherSample() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, components: S2\n" - + "S2, data sets: DS2\n" - + "S3, data sets: DS1\n" - + "DS1, components: DS2\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - prepareTrashSamples(deletion, false, g.s(2)); - - failTrashSample(g.s(1), g.ds(2), g.ds(1), g.s(3)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashSampleWithComponentWithDataSetWithComponentOfAnotherExperiment() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("E1, data sets: DS1\n" - + "S1, components: S2\n" - + "S2, data sets: DS2\n" - + "DS1, components: DS2\n"); - prepareEntityGraph(g); - final DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - prepareTrashSamples(deletion, false, g.s(2)); - - failTrashSample(g.s(1), g.ds(2), g.ds(1), g.e(1)); - - context.assertIsSatisfied(); - } - - @Test - public final void testTrashDataSets() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, data sets: DS1 DS2 DS3 DS4 DS5 DS6\n" - + "DS1, components: DS5\n" - + "DS2, components: DS6\n"); - prepareEntityGraph(g); - DeletionPE deletion = createDeletion(); - prepareTrashDataSets(deletion, true, g.ds(1), g.ds(2), g.ds(3)); - prepareTrashDataSets(deletion, false, g.ds(5), g.ds(6)); - - trashBO.trashDataSets(asIds(g.ds(1), g.ds(2), g.ds(3))); - - context.assertIsSatisfied(); - } - - @Test - public void testTrashDataSetsWithDataSetInAContainer() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, data sets: DS1 DS2 DS3\n" - + "DS1, components: DS2\n" - + "DS2, components: DS3\n"); - prepareEntityGraph(g); - DeletionPE deletion = createDeletion(); - prepareTrashDataSets(deletion, true, g.ds(2)); - prepareTrashDataSets(deletion, false, g.ds(3)); - - trashBO.trashDataSets(asIds(g.ds(2))); - - context.assertIsSatisfied(); - } - - @Test - public void testTrashDataSetsWithDataSetComponentIndirectlyDependentOnOutsideContainer() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, data sets: DS1 DS2 DS3 DS4 DS5 DS6\n" - + "DS1, components: DS3 DS4 DS5\n" - + "DS2, components: DS4\n" - + "DS4, components: DS5 DS6\n"); - prepareEntityGraph(g); - DeletionPE deletion = createDeletion(); - prepareTrashDataSets(deletion, true, g.ds(1)); - prepareTrashDataSets(deletion, false, g.ds(3)); - - trashBO.trashDataSets(asIds(g.ds(1))); - - context.assertIsSatisfied(); - } - - @Test - public void testTrashSampleWithDataSetWithDataSetComponentIndirectlyDependentOnOutsideContainer() - { - EntityGraphGenerator g = new EntityGraphGenerator(); - g.parse("S1, data sets: DS1 DS3 DS4 DS5 DS6\n" - + "S2, data sets: DS2\n" - + "DS1, components: DS3 DS4 DS5\n" - + "DS2, components: DS4\n" - + "DS4, components: DS5 DS6\n"); - prepareEntityGraph(g); - DeletionPE deletion = createDeletion(); - prepareTrashSamples(deletion, true, g.s(1)); - - failTrashSample(g.s(1), g.ds(4), g.ds(2), g.s(2)); - - context.assertIsSatisfied(); - } - @Test public final void testTrashUnavailableDataSets() { @@ -628,85 +231,6 @@ public final class TrashBOTest extends AbstractBOTest context.assertIsSatisfied(); } - private void prepareTrashExperiments(DeletionPE deletion, boolean isOriginalDeletion, - ExperimentNode... experiments) - { - prepareTrash(deletion, EntityKind.EXPERIMENT, isOriginalDeletion, asIds(experiments)); - } - - private void prepareTrashSamples(DeletionPE deletion, boolean isOriginalDeletion, - SampleNode... samples) - { - prepareTrash(deletion, EntityKind.SAMPLE, isOriginalDeletion, asIds(samples)); - } - - private void prepareTrashDataSets(DeletionPE deletion, boolean isOriginalDeletion, - DataSetNode... dataSets) - { - prepareTrash(deletion, EntityKind.DATA_SET, isOriginalDeletion, asIds(dataSets)); - } - - private void prepareTrash(final DeletionPE deletion, final EntityKind entityKind, - final boolean isOriginalDeletion, final List<TechId> entityIds) - { - context.checking(new Expectations() - { - { - one(deletionDAO).trash(entityKind, entityIds, deletion, isOriginalDeletion); - will(returnValue(entityIds.size())); - } - }); - } - - private void failTrashExperiment(ExperimentNode experimentNode, DataSetNode originalDataSet, - DataSetNode relatedDataSet, EntityNode outsiderNode) - { - try - { - trashBO.trashExperiments(asIds(experimentNode)); - fail("UserFailureException expected"); - } catch (UserFailureException ex) - { - assertExceptionMessage(originalDataSet, relatedDataSet, outsiderNode, ex); - } - } - - private void failTrashSample(SampleNode sampleNode, SampleNode relatedSample, EntityNode outsiderNode) - { - try - { - trashBO.trashSamples(asIds(sampleNode)); - fail("UserFailureException expected"); - } catch (UserFailureException ex) - { - String outsiderType = outsiderNode instanceof ExperimentNode ? "experiment" : "sample"; - assertEquals("The sample " + relatedSample.getIdentifier() + " belongs to " + outsiderType + " " - + outsiderNode.getCode() + " is outside the deletion set.", ex.getMessage()); - } - } - - private void failTrashSample(SampleNode sampleNode, DataSetNode originalDataSet, - DataSetNode relatedDataSet, EntityNode outsiderNode) - { - try - { - trashBO.trashSamples(asIds(sampleNode)); - fail("UserFailureException expected"); - } catch (UserFailureException ex) - { - assertExceptionMessage(originalDataSet, relatedDataSet, outsiderNode, ex); - } - } - - private void assertExceptionMessage(DataSetNode originalDataSet, DataSetNode relatedDataSet, - EntityNode outsiderNode, UserFailureException ex) - { - String outsiderType = outsiderNode instanceof ExperimentNode ? "experiment" : "sample"; - assertEquals("The data set " + originalDataSet.getCode() + " is a component of the data set " - + relatedDataSet.getCode() + " which belongs to " + outsiderType + " " - + outsiderNode.getIdentifier() + " outside the deletion set.", ex.getMessage()); - } - private void prepareEntityGraph(EntityGraphGenerator g) { g.assertConsistency(); diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5e76e16e505afbea767f1ae98ac66ddb7666884e --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java @@ -0,0 +1,553 @@ +/* + * Copyright 2015 ETH Zuerich, SIS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.systemtest; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.DataSetNode; +import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.EntityGraphGenerator; +import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.EntityNode; +import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.ExperimentNode; +import ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph.SampleNode; +import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DeletionType; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory; +import ch.systemsx.cisd.openbis.systemtest.base.BaseTest; + +/** + * + * + * @author Franz-Josef Elmer + */ +public class EntityDeletionTest extends BaseTest +{ + @Test + public final void testTrashExperimentsWithOneLevelOfDependencies() + { + EntityGraphGenerator g = parseAndCreateGraph( + "E1, samples: S50, data sets: DS60 DS61\n" + + "E2, samples: S51\n" + + "E3\n" + ); + + deleteExperiments(g.e(1), g.e(2), g.e(3)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.e(1), g.e(2), g.e(3)); + assertDeleted(g.s(50), g.s(51)); + assertDeleted(g.ds(60), g.ds(61)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashExperimentsWithDeepDependencies() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S50, data sets: DS60 DS61 DS70 DS71\n" + + "E2, samples: S51\n" + + "E3\n" + + "S50, components: S52, data sets: DS70 DS71\n" + + "S51, components: S53\n" + ); + + deleteExperiments(g.e(1), g.e(2), g.e(3)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.e(1), g.e(2), g.e(3)); + assertDeleted(g.s(50), g.s(51), g.s(52), g.s(53)); + assertDeleted(g.ds(60), g.ds(61), g.ds(70), g.ds(71)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashExperimentWithSamplesAndDataSetsAndNoExternalLinks() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n" + + "E2, data sets: DS2\n" + + "S1, data sets: DS1\n" + + "DS1, components: DS2\n"); + + deleteExperiments(g.e(1), g.e(2)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.e(1), g.e(2)); + assertDeleted(g.s(1)); + assertDeleted(g.ds(1), g.ds(2)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashExperimentWithARelatedDataSetComponentWhichBelongsToAnExternalExperiment() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n" + + "E2, data sets: DS2\n" + + "S1, data sets: DS1\n" + + "DS1, components: DS2\n"); + + deleteExperiments(g.e(1)); + + assertEquals("E2, data sets: DS2\n", renderGraph(g)); + assertDeleted(g.e(1)); + assertDeleted(g.s(1)); + assertDeleted(g.ds(1)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashExperimentWithARelatedDataSetInAnExternalContainer() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n" + + "E2, data sets: DS2\n" + + "S1, data sets: DS1\n" + + "DS1, components: DS2\n"); + + failTrashExperiment(g.e(2), g.ds(2), g.ds(1), g.s(1)); + + assertEquals("E1, samples: S1, data sets: DS1\n" + + "E2, data sets: DS2\n" + + "S1, data sets: DS1\n" + + "DS1, components: DS2\n", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public void testTrashPublishedExperiment() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, data sets: DS3 DS4 DS5 DS6\n" + + "E2, data sets: DS1 DS2\n" + + "DS1, components: DS3\n" + + "DS2, components: DS6\n" + + "DS3, components: DS4\n" + + "DS5, parents: DS6\n"); + + deleteExperiments(g.e(2)); + + assertEquals("E1, data sets: DS3 DS4 DS5 DS6\n" + + "DS3, components: DS4\n" + + "DS5, parents: DS6\n", renderGraph(g)); + assertDeleted(g.e(2)); + assertDeleted(g.ds(1), g.ds(2)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashPublishedExperimentWithOrginalExperimentWithSamples() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS2 DS3\n" + + "E2, data sets: DS1\n" + + "S1, data sets: DS2 DS3\n" + + "DS1, components: DS2\n" + + "DS2, components: DS3\n" + ); + + deleteExperiments(g.e(2)); + + assertEquals("E1, samples: S1, data sets: DS2 DS3\n" + + "S1, data sets: DS2 DS3\n" + + "DS2, components: DS3\n", renderGraph(g)); + assertDeleted(g.e(2)); + assertDeleted(g.ds(1)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashOrginalExperimentWithSample() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS2 DS3\n" + + "E2, data sets: DS1\n" + + "S1, data sets: DS2 DS3\n" + + "DS1, components: DS2\n" + + "DS2, components: DS3\n" + ); + + failTrashExperiment(g.e(1), g.ds(2), g.ds(1), g.e(2)); + + assertEquals("E1, samples: S1, data sets: DS2 DS3\n" + + "E2, data sets: DS1\n" + + "S1, data sets: DS2 DS3\n" + + "DS1, components: DS2\n" + + "DS2, components: DS3\n", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashExperimentsWithContainerDataSetWithPhysicalDataSetFromAnotherExperiment() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, data sets: DS1\n" + + "E2, data sets: DS2\n" + + "DS1, components: DS2\n"); + + deleteExperiments(g.e(1)); + + assertEquals("E2, data sets: DS2\n", renderGraph(g)); + assertDeleted(g.e(1)); + assertDeleted(g.ds(1)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSamplesAlreadyTrashed() + { + EntityGraphGenerator g = parseAndCreateGraph("S1\nS2\n"); + + + assertEquals("", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSamplesWithOneLevelOfDependencies() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, components: S20\n" + + "S3, data sets: DS60[NET]\n" + + "S20, data sets: DS61[NET]\n"); + + deleteSamples(g.s(1), g.s(3)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.s(1), g.s(3), g.s(20)); + assertDeleted(g.ds(60), g.ds(61)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSampleWithTwoLevelOfDependencies() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, components: S2 S3, data sets: DS1[NET]\n" + + "S2, data sets: DS2[NET]\n" + + "S3, data sets: DS3[NET]\n"); + + deleteSamples(g.s(1)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.s(1), g.s(2), g.s(3)); + assertDeleted(g.ds(1), g.ds(2), g.ds(3)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSampleWithAComponentWithAContainerDataSetWithAComponentDataSetOfFirstSample() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, components: S2, data sets: DS2[NET]\n" + + "S2, data sets: DS1[NECT]\n" + + "DS1[NECT], components: DS2[NET]\n"); + + deleteSamples(g.s(1)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.s(1), g.s(2)); + assertDeleted(g.ds(1), g.ds(2)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSampleWithAComponentWithAComponentDataSetOfAContainerDataSetOfFirstSample() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, components: S2, data sets: DS1[NECT]\n" + + "S2, data sets: DS2[NET]\n" + + "DS1[NECT], components: DS2[NET]\n"); + + deleteSamples(g.s(1)); + + assertEquals("", renderGraph(g)); + assertDeleted(g.s(1), g.s(2)); + assertDeleted(g.ds(1), g.ds(2)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSampleWithAnExperimentSampleWithADataSet() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S2, data sets: DS1\n" + + "S1, components: S2\n" + + "S2, data sets: DS1\n"); + + failTrashSample(g.s(1), g.s(2), g.e(1)); + + assertEquals("E1, samples: S2, data sets: DS1\n" + + "S1, components: S2\n" + + "S2, data sets: DS1\n", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashExperimentSampleWithADataSet() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n" + + "S1, data sets: DS1\n"); + + deleteSamples(g.s(1)); + + assertEquals("", renderGraph(g)); + assertModified(g.e(1)); + assertDeleted(g.s(1)); + assertDeleted(g.ds(1)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSampleWithComponentWithDataSetWithComponentOfAnotherSample() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, components: S2\n" + + "S2, data sets: DS2[NET]\n" + + "S3, data sets: DS1[NECT]\n" + + "DS1[NECT], components: DS2[NET]\n"); + + failTrashSample(g.s(1), g.ds(2), g.ds(1), g.s(3)); + + assertEquals("S1, components: S2\n" + + "S2, data sets: DS2[NET]\n" + + "S3, data sets: DS1[NECT]\n" + + "DS1[NECT], components: DS2[NET]\n", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashSampleWithComponentWithDataSetWithComponentOfAnotherExperiment() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, data sets: DS1\n" + + "S1, components: S2\n" + + "S2, data sets: DS2[NET]\n" + + "DS1, components: DS2[NET]\n"); + + failTrashSample(g.s(1), g.ds(2), g.ds(1), g.e(1)); + + assertEquals("E1, data sets: DS1\n" + + "S1, components: S2\n" + + "S2, data sets: DS2[NET]\n" + + "DS1, components: DS2[NET]\n", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public void testTrashSampleWithDataSetWithDataSetComponentIndirectlyDependentOnOutsideContainer() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, data sets: DS1[NECT] DS3[NET] DS4[NECT] DS5[NET] DS6[NET]\n" + + "S2, data sets: DS2[NECT]\n" + + "DS1[NECT], components: DS3[NET] DS4[NECT] DS5[NET]\n" + + "DS2[NECT], components: DS4[NECT]\n" + + "DS4[NECT], components: DS5[NET] DS6[NET]\n"); + + failTrashSample(g.s(1), g.ds(4), g.ds(2), g.s(2)); + + assertEquals("S1, data sets: DS1[NECT] DS3[NET] DS4[NECT] DS5[NET] DS6[NET]\n" + + "S2, data sets: DS2[NECT]\n" + + "DS1[NECT], components: DS3[NET] DS4[NECT] DS5[NET]\n" + + "DS2[NECT], components: DS4[NECT]\n" + + "DS4[NECT], components: DS5[NET] DS6[NET]\n", renderGraph(g)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashDataSets() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, data sets: DS1[NECT] DS2[NECT] DS3[NET] DS4[NET] DS5[NET] DS6[NET]\n" + + "DS1[NECT], components: DS5[NET]\n" + + "DS2[NECT], components: DS6[NET]\n"); + + deleteDataSets(g.ds(1), g.ds(2), g.ds(3)); + + assertEquals("S1, data sets: DS4[NET]\n", renderGraph(g)); + assertModified(g.s(1)); + assertDeleted(g.ds(1), g.ds(2), g.ds(3), g.ds(5), g.ds(6)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public final void testTrashDataSetAndComponentsBelongingToSameExperiment() + { + EntityGraphGenerator g = parseAndCreateGraph("E1, data sets: DS1 DS2\n" + + "E2, data sets: DS3\n" + + "DS1, components: DS2 DS3\n"); + + deleteDataSets(g.ds(1)); + + assertEquals("E2, data sets: DS3\n", renderGraph(g)); + assertModified(g.e(1)); + assertDeleted(g.ds(1), g.ds(2)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public void testTrashDataSetsWithDataSetInAContainer() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, data sets: DS1[NECT] DS2[NECT] DS3[NET]\n" + + "DS1[NECT], components: DS2[NECT]\n" + + "DS2[NECT], components: DS3[NET]\n"); + + deleteDataSets(g.ds(2)); + + assertEquals("S1, data sets: DS1[NECT]\n", renderGraph(g)); + assertModified(g.s(1)); + assertModified(g.ds(1)); + assertDeleted(g.ds(2), g.ds(3)); + assertUnmodifiedAndUndeleted(g); + } + + @Test + public void testTrashDataSetsWithDataSetComponentIndirectlyDependentOnOutsideContainer() + { + EntityGraphGenerator g = parseAndCreateGraph("S1, data sets: DS1[NECT] DS2[NECT] DS3[NET] DS4[NECT] DS5[NET] DS6[NET]\n" + + "DS1[NECT], components: DS3[NET] DS4[NECT] DS5[NET]\n" + + "DS2[NECT], components: DS4[NECT]\n" + + "DS4[NECT], components: DS5[NET] DS6[NET]\n"); + + deleteDataSets(g.ds(1)); + + assertEquals("S1, data sets: DS2[NECT] DS4[NECT] DS5[NET] DS6[NET]\n" + + "DS2[NECT], components: DS4[NECT]\n" + + "DS4[NECT], components: DS5[NET] DS6[NET]\n", renderGraph(g)); + assertModified(g.s(1)); + assertDeleted(g.ds(1), g.ds(3)); + assertUnmodifiedAndUndeleted(g); + } + + private void failTrashExperiment(ExperimentNode experimentNode, DataSetNode originalDataSet, + DataSetNode relatedDataSet, EntityNode outsiderNode) + { + try + { + deleteExperiments(experimentNode); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertExceptionMessage(originalDataSet, relatedDataSet, outsiderNode, ex); + } + } + + private void failTrashSample(SampleNode sampleNode, SampleNode relatedSample, EntityNode outsiderNode) + { + try + { + deleteSamples(sampleNode); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("The sample " + entityGraphManager.getSample(relatedSample).getIdentifier() + + " belongs to " + renderOutsider(outsiderNode) + " is outside the deletion set.", + ex.getMessage()); + } + } + + private void failTrashSample(SampleNode sampleNode, DataSetNode originalDataSet, + DataSetNode relatedDataSet, EntityNode outsiderNode) + { + try + { + deleteSamples(sampleNode); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertExceptionMessage(originalDataSet, relatedDataSet, outsiderNode, ex); + } + } + + private void assertExceptionMessage(DataSetNode originalDataSet, DataSetNode relatedDataSet, + EntityNode outsiderNode, UserFailureException ex) + { + assertEquals("The data set " + entityGraphManager.getDataSetCodeOrNull(originalDataSet) + + " is a component of the data set " + entityGraphManager.getDataSetCodeOrNull(relatedDataSet) + + " which belongs to " + renderOutsider(outsiderNode) + " outside the deletion set.", + ex.getMessage()); + } + + private String renderOutsider(EntityNode outsiderNode) + { + String outsiderType; + String outsiderIdentifier; + if (outsiderNode instanceof ExperimentNode) + { + outsiderType = "experiment"; + outsiderIdentifier = entityGraphManager.getExperimentIdentifierOrNull((ExperimentNode) outsiderNode); + } else + { + outsiderType = "sample"; + outsiderIdentifier = entityGraphManager.getSample((SampleNode) outsiderNode).getIdentifier(); + } + String outsider = outsiderType + " " + + outsiderIdentifier; + return outsider; + } + + private void deleteExperiments(ExperimentNode...experimentNodes) + { + List<String> experimentIdentifiers = new ArrayList<String>(); + for (ExperimentNode experimentNode : experimentNodes) + { + experimentIdentifiers.add(entityGraphManager.getExperimentIdentifierOrNull(experimentNode)); + } + deleteExperiments(experimentIdentifiers, createAdminUser()); + } + + private void deleteSamples(SampleNode...sampleNodes) + { + List<String> samplePermIds = new ArrayList<String>(); + for (SampleNode sampleNode : sampleNodes) + { + samplePermIds.add(entityGraphManager.getSamplePermIdOrNull(sampleNode)); + } + deleteSamples(samplePermIds, createAdminUser()); + } + + private void deleteDataSets(DataSetNode...dataSetNodes) + { + List<String> dataSetCodes = new ArrayList<String>(); + for (DataSetNode dataSetNode : dataSetNodes) + { + dataSetCodes.add(entityGraphManager.getDataSetCodeOrNull(dataSetNode)); + } + deleteDataSets(dataSetCodes, createAdminUser()); + } + + private String createAdminUser() + { + return create(aSession().withInstanceRole(RoleCode.ADMIN)); + } + + protected void deleteExperiments(List<String> experimentIdentifiers, String userSessionToken) + { + List<ExperimentIdentifier> identifiers = new ArrayList<ExperimentIdentifier>(); + for (String identifier : experimentIdentifiers) + { + identifiers.add(ExperimentIdentifierFactory.parse(identifier)); + } + List<TechId> experimentIds = TechId.createList(commonServer.listExperiments(userSessionToken, identifiers)); + commonServer.deleteExperiments(userSessionToken, experimentIds, "test", DeletionType.TRASH); + } + + protected void deleteSamples(List<String> samplePermIds, String userSessionToken) + { + Sample[] samples = loadSamples(samplePermIds); + commonServer.deleteSamples(userSessionToken, TechId.createList(Arrays.asList(samples)), "test", DeletionType.TRASH); + } + + protected void deleteDataSets(List<String> dataSetCodes, String userSessionToken) + { + commonServer.deleteDataSets(userSessionToken, dataSetCodes, "test", DeletionType.TRASH, true); + } + +} diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java index ea4b536bb7ae87acc458366ead650f4b247c4c8d..ad6a27abc0d9771378cc9fc6e257299f533d5d3f 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java @@ -493,21 +493,47 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT entityGraphManager.assertModified(experimentNodes); } + protected void assertDeleted(ExperimentNode... experimentNodes) + { + entityGraphManager.assertDeleted(experimentNodes); + } + protected void assertModified(SampleNode... sampleNodes) { entityGraphManager.assertModified(sampleNodes); } + protected void assertDeleted(SampleNode... sampleNodes) + { + entityGraphManager.assertDeleted(sampleNodes); + } + protected void assertModified(DataSetNode... dataSetNodes) { entityGraphManager.assertModified(dataSetNodes); } + protected void assertDeleted(DataSetNode... dataSetNodes) + { + entityGraphManager.assertDeleted(dataSetNodes); + } + protected void assertUnmodified(EntityGraphGenerator g) { entityGraphManager.assertUnmodified(g); } + protected void assertUndeleted(EntityGraphGenerator g) + { + entityGraphManager.assertUndeleted(g); + } + + protected void assertUnmodifiedAndUndeleted(EntityGraphGenerator g) + { + assertUnmodified(g); + assertUndeleted(g); + } + protected String getIdentifierOfDefaultProject() { return defaultProject.getIdentifier(); diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java index 425fee7d6f97fa38795493bdb4befea737f6d635..7e917a592c514004bd528c5129d090e3c05aef45 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java @@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.systemtest.entitygraph; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.fail; import java.util.ArrayList; import java.util.Arrays; @@ -142,21 +143,41 @@ public class EntityGraphManager repository.assertModified(experimentNodes); } + public void assertDeleted(ExperimentNode[] experimentNodes) + { + repository.assertDeleted(experimentNodes); + } + public void assertModified(SampleNode[] sampleNodes) { repository.assertModified(sampleNodes); } + public void assertDeleted(SampleNode[] sampleNodes) + { + repository.assertDeleted(sampleNodes); + } + public void assertModified(DataSetNode[] dataSetNodes) { repository.assertModified(dataSetNodes); } + public void assertDeleted(DataSetNode[] dataSetNodes) + { + repository.assertDeleted(dataSetNodes); + } + public void assertUnmodified(EntityGraphGenerator g) { repository.assertUnmodified(g); } + public void assertUndeleted(EntityGraphGenerator g) + { + repository.assertUndeleted(g); + } + public String getIdentifierOfDefaultProject() { return defaultProject.getIdentifier(); @@ -624,16 +645,19 @@ public class EntityGraphManager private Map<Long, Experiment> experimentsNodeToDtoMap = new TreeMap<Long, Experiment>(); private Map<Long, ModificationInfo> experimentModificationInfoByNodeId = new HashMap<Long, ModificationInfo>(); private Set<ExperimentNode> modifiedExperimentNodes = new HashSet<ExperimentNode>(); + private Set<ExperimentNode> deletedExperimentNodes = new HashSet<ExperimentNode>(); private Map<Long, Sample> samplesNodeToDtoMap = new TreeMap<Long, Sample>(); private Map<Long, SampleNode> samplesDtoToNodeMap = new TreeMap<Long, SampleNode>(); private Map<Long, ModificationInfo> sampleModificationInfoByNodeId = new HashMap<Long, ModificationInfo>(); private Set<SampleNode> modifiedSampleNodes = new HashSet<SampleNode>(); + private Set<SampleNode> deletedSampleNodes = new HashSet<SampleNode>(); private Map<Long, AbstractExternalData> dataSetsNodeToDtoMap = new TreeMap<Long, AbstractExternalData>(); private Map<Long, DataSetNode> dataSetsDtoToNodeMap = new TreeMap<Long, DataSetNode>(); private Map<Long, ModificationInfo> dataSetModificationInfoByNodeId = new HashMap<Long, ModificationInfo>(); private Set<DataSetNode> modifiedDataSetNodes = new HashSet<DataSetNode>(); + private Set<DataSetNode> deletedDataSetNodes = new HashSet<DataSetNode>(); private Map<Long, Set<Long>> experimentSamplesMap = new HashMap<Long, Set<Long>>(); private Map<Long, Set<Long>> childrenSamplesMap = new HashMap<Long, Set<Long>>(); @@ -901,6 +925,12 @@ public class EntityGraphManager modifiedExperimentNodes.addAll(Arrays.asList(experimentNodes)); } + public void assertDeleted(ExperimentNode...experimentNodes) + { + assertDeleted(experimentsNodeToDtoMap, Arrays.<EntityNode>asList(experimentNodes)); + deletedExperimentNodes.addAll(Arrays.asList(experimentNodes)); + } + void put(SampleNode sampleNode, Sample sample) { samplesNodeToDtoMap.put(sampleNode.getId(), sample); @@ -914,6 +944,12 @@ public class EntityGraphManager modifiedSampleNodes.addAll(Arrays.asList(sampleNodes)); } + public void assertDeleted(SampleNode...sampleNodes) + { + assertDeleted(samplesNodeToDtoMap, Arrays.<EntityNode>asList(sampleNodes)); + deletedSampleNodes.addAll(Arrays.asList(sampleNodes)); + } + void put(DataSetNode dataSetNode, AbstractExternalData dataSet) { dataSetsNodeToDtoMap.put(dataSetNode.getId(), dataSet); @@ -927,6 +963,12 @@ public class EntityGraphManager modifiedDataSetNodes.addAll(Arrays.asList(dataSetNodes)); } + public void assertDeleted(DataSetNode...dataSetNodes) + { + assertDeleted(dataSetsNodeToDtoMap, Arrays.<EntityNode>asList(dataSetNodes)); + deletedDataSetNodes.addAll(Arrays.asList(dataSetNodes)); + } + public void assertUnmodified(EntityGraphGenerator g) { Set<EntityNode> unmodifiedExperimentNodes = new HashSet<EntityNode>(g.getExperiments().values()); @@ -978,6 +1020,50 @@ public class EntityGraphManager } } } + + private void assertDeleted(Map<Long, ? extends CodeWithRegistrationAndModificationDate<?>> nodeToDtoMap, + Collection<EntityNode> entityNodes) + { + for (EntityNode node : entityNodes) + { + if (nodeToDtoMap.containsKey(node.getId())) + { + fail("Node " + node.getIdentifierAndType() + " hasn't been deleted."); + } + } + } + + public void assertUndeleted(EntityGraphGenerator g) + { + Set<EntityNode> undeletedExperimentNodes = new HashSet<EntityNode>(g.getExperiments().values()); + undeletedExperimentNodes.removeAll(deletedExperimentNodes); + assertUndeleted(experimentsNodeToDtoMap, undeletedExperimentNodes); + Set<EntityNode> undeletedSampleNodes = new HashSet<EntityNode>(g.getSamples().values()); + undeletedSampleNodes.removeAll(deletedSampleNodes); + assertUndeleted(samplesNodeToDtoMap, undeletedSampleNodes); + Set<EntityNode> undeletedDataSetNodes = new HashSet<EntityNode>(g.getDataSets().values()); + undeletedDataSetNodes.removeAll(deletedDataSetNodes); + assertUndeleted(dataSetsNodeToDtoMap, undeletedDataSetNodes); + } + + private void assertUndeleted(Map<Long, ? extends CodeWithRegistrationAndModificationDate<?>> nodeToDtoMap, + Collection<EntityNode> entityNodes) + { + List<String> deletedNodes = new ArrayList<String>(); + for (EntityNode node : entityNodes) + { + if (nodeToDtoMap.containsKey(node.getId()) == false) + { + deletedNodes.add(node.getIdentifierAndType()); + } + } + if (deletedNodes.isEmpty() == false) + { + Collections.sort(deletedNodes); + fail("The following nodes have been deleted: " + deletedNodes); + } + } + Set<SampleNode> getSampleNodes(ExperimentNode experimentNode) {