diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApi.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApi.java index 3c9ae2e608d48a841dea79a4d9e40a8116306e29..4f649fbe2d6be108d1b18ed9242dc35e54092619 100644 --- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApi.java +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApi.java @@ -42,6 +42,7 @@ import ch.ethz.sis.openbis.generic.server.api.v3.executor.experiment.IUpdateExpe import ch.ethz.sis.openbis.generic.server.api.v3.executor.material.ICreateMaterialExecutor; import ch.ethz.sis.openbis.generic.server.api.v3.executor.material.IDeleteMaterialExecutor; import ch.ethz.sis.openbis.generic.server.api.v3.executor.material.IMapMaterialByIdExecutor; +import ch.ethz.sis.openbis.generic.server.api.v3.executor.material.IUpdateMaterialExecutor; import ch.ethz.sis.openbis.generic.server.api.v3.executor.project.ICreateProjectExecutor; import ch.ethz.sis.openbis.generic.server.api.v3.executor.project.IMapProjectByIdExecutor; import ch.ethz.sis.openbis.generic.server.api.v3.executor.sample.ICreateSampleExecutor; @@ -79,6 +80,7 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.Experimen import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.ExperimentUpdate; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialCreation; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialUpdate; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.project.Project; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.project.ProjectCreation; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.Sample; @@ -167,6 +169,9 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi> @Autowired private IUpdateSampleExecutor updateSampleExecutor; + @Autowired + private IUpdateMaterialExecutor updateMaterialExecutor; + @Autowired private IUpdateDataSetExecutor updateDataSetExecutor; @@ -435,6 +440,26 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi> } } + @Override + @Transactional + @RolesAllowed( + { RoleWithHierarchy.INSTANCE_ADMIN, RoleWithHierarchy.INSTANCE_ETL_SERVER }) + @Capability("UPDATE_MATERIAL") + @DatabaseUpdateModification(value = ObjectKind.MATERIAL) + public void updateMaterials(String sessionToken, List<MaterialUpdate> updates) + { + Session session = getSession(sessionToken); + OperationContext context = new OperationContext(session); + + try + { + updateMaterialExecutor.update(context, updates); + } catch (Throwable t) + { + throw ExceptionUtils.create(context, t); + } + } + @Override @Transactional @RolesAllowed({ RoleWithHierarchy.SPACE_POWER_USER, RoleWithHierarchy.SPACE_ETL_SERVER }) diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApiLogger.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApiLogger.java index 2ab2f86d33bfc530c054cf15ffae59c8525f6c79..4b848d0a08839e7ac36f0f90642a18100a35d8fd 100644 --- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApiLogger.java +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/ApplicationServerApiLogger.java @@ -33,6 +33,7 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.Experimen import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.ExperimentUpdate; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialCreation; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialUpdate; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.project.Project; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.project.ProjectCreation; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.Sample; @@ -173,6 +174,12 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements logAccess(sessionToken, "update-data-sets", "DATA_SET_UPDATES(%s)", dataSetUpdates); } + @Override + public void updateMaterials(String sessionToken, List<MaterialUpdate> materialUpdates) + { + logAccess(sessionToken, "update-materials", "MATERIAL_UPDATES(%s)", materialUpdates); + } + @Override public Map<ISpaceId, Space> mapSpaces(String sessionToken, List<? extends ISpaceId> spaceIds, SpaceFetchOptions fetchOptions) { diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/material/IUpdateMaterialExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/material/IUpdateMaterialExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..97a94a521de7d1e43f298b3ef4506133279aa873 --- /dev/null +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/material/IUpdateMaterialExecutor.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 ETH Zuerich, Scientific IT Services + * + * 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.ethz.sis.openbis.generic.server.api.v3.executor.material; + +import ch.ethz.sis.openbis.generic.server.api.v3.executor.entity.IUpdateEntityExecutor; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialUpdate; + +/** + * @author Jakub Straszewski + */ +public interface IUpdateMaterialExecutor extends IUpdateEntityExecutor<MaterialUpdate> +{ + +} diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/material/UpdateMaterialExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/material/UpdateMaterialExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..d14bcd1b0261d4687d0296ab6cb558e1a68516c6 --- /dev/null +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/material/UpdateMaterialExecutor.java @@ -0,0 +1,139 @@ +/* + * Copyright 2014 ETH Zuerich, CISD + * + * 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.ethz.sis.openbis.generic.server.api.v3.executor.material; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Component; + +import ch.ethz.sis.openbis.generic.server.api.v3.executor.IOperationContext; +import ch.ethz.sis.openbis.generic.server.api.v3.executor.entity.AbstractUpdateEntityExecutor; +import ch.ethz.sis.openbis.generic.server.api.v3.executor.property.IUpdateEntityPropertyExecutor; +import ch.ethz.sis.openbis.generic.server.api.v3.executor.tag.IUpdateTagForEntityExecutor; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialUpdate; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.material.IMaterialId; +import ch.ethz.sis.openbis.generic.shared.api.v3.exceptions.UnauthorizedObjectAccessException; +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator; +import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator; +import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; +import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityPropertiesHolder; +import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind; + +/** + * @author Jakub Straszewski + */ +@Component +public class UpdateMaterialExecutor extends AbstractUpdateEntityExecutor<MaterialUpdate, MaterialPE, IMaterialId> implements IUpdateMaterialExecutor +{ + + @Autowired + private IDAOFactory daoFactory; + + @Autowired + private IMapMaterialByIdExecutor mapMaterialByIdExecutor; + + @Autowired + private IUpdateEntityPropertyExecutor updateEntityPropertyExecutor; + + @Autowired + private IUpdateTagForEntityExecutor updateTagForEntityExecutor; + + @Autowired + private IVerifyMaterialExecutor verifyMaterialExecutor; + + @Override + protected IMaterialId getId(MaterialUpdate update) + { + return update.getMaterialId(); + } + + @Override + protected void checkData(IOperationContext context, MaterialUpdate update) + { + if (update.getMaterialId() == null) + { + throw new UserFailureException("Sample id cannot be null."); + } + } + + @Override + protected void checkAccess(IOperationContext context, IMaterialId id, MaterialPE entity) + { + if (false == new SampleByIdentiferValidator().doValidation(context.getSession().tryGetPerson(), entity)) + { + throw new UnauthorizedObjectAccessException(id); + } + } + + @Override + protected void checkBusinessRules(IOperationContext context, Collection<MaterialPE> entities) + { + verifyMaterialExecutor.verify(context, entities); + } + + @Override + protected void updateBatch(IOperationContext context, Map<MaterialUpdate, MaterialPE> entitiesMap) + { + Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>(); + for (Map.Entry<MaterialUpdate, MaterialPE> entry : entitiesMap.entrySet()) + { + MaterialUpdate update = entry.getKey(); + MaterialPE entity = entry.getValue(); + updateTagForEntityExecutor.update(context, entity, update.getTagIds()); + propertyMap.put(entity, update.getProperties()); + } + + updateEntityPropertyExecutor.update(context, propertyMap); + } + + @Override + protected void updateAll(IOperationContext context, Map<MaterialUpdate, MaterialPE> entitiesMap) + { + } + + @Override + protected Map<IMaterialId, MaterialPE> map(IOperationContext context, Collection<IMaterialId> ids) + { + return mapMaterialByIdExecutor.map(context, ids); + } + + @Override + protected List<MaterialPE> list(IOperationContext context, Collection<Long> ids) + { + return daoFactory.getMaterialDAO().listMaterialsById(ids); + } + + @Override + protected void save(IOperationContext context, List<MaterialPE> entities, boolean clearCache) + { + daoFactory.getMaterialDAO().createOrUpdateMaterials(entities); + } + + @Override + protected void handleException(DataAccessException e) + { + DataAccessExceptionTranslator.throwException(e, EntityKind.SAMPLE.getLabel(), EntityKind.SAMPLE); + } + +} diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/CreateMaterialTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/CreateMaterialTest.java index 2781aafbc44aa9c65320cd18d7b5bb1ee1b02099..f46266234e219e2acb74d39747da18864fa577ee 100644 --- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/CreateMaterialTest.java +++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/CreateMaterialTest.java @@ -57,15 +57,70 @@ public class CreateMaterialTest extends AbstractSampleTest AssertionUtil.assertCollectionSize(map.values(), 2); Material material = map.get(new MaterialPermId("1982", "GENE")); - assertEquals(material.getCode(), "1982"); assertEquals(material.getPermId().getTypeCode(), "GENE"); + + material = map.get(new MaterialPermId("1984", "GENE")); + assertEquals(material.getCode(), "1984"); + assertEquals(material.getPermId().getTypeCode(), "GENE"); + } + + // @Test broken + public void testCreateTwoMaterialsWithMaterialLinks() + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + MaterialPermId m1id = new MaterialPermId("FIRST", "SELF_REF"); + MaterialCreation m1 = materialCreation(m1id); + HashMap<String, String> properties1 = new HashMap<String, String>(); + properties1.put("ANY_MATERIAL", "SECOND (SELF_REF)"); + properties1.put("DESCRIPTION", "mandatory material decsription"); + m1.setProperties(properties1); + + MaterialPermId m2id = new MaterialPermId("SECOND", "SELF_REF"); + MaterialCreation m2 = materialCreation(m2id); + HashMap<String, String> properties2 = new HashMap<String, String>(); + properties2.put("ANY_MATERIAL", "FIRST (SELF_REF)"); + properties2.put("DESCRIPTION", "mandatory material decsription"); + m2.setProperties(properties2); + + List<MaterialPermId> materialIds = v3api.createMaterials(sessionToken, Arrays.asList(m1, m2)); + + AssertionUtil.assertCollectionContainsOnly(materialIds, m1id, m2id); + + // circular materialProperties + MaterialFetchOptions fetchOptions = new MaterialFetchOptions(); + fetchOptions.withMaterialPropertiesUsing(fetchOptions); + + Map<IMaterialId, Material> map = v3api.mapMaterials(sessionToken, Arrays.asList(m1id), fetchOptions); + + AssertionUtil.assertCollectionSize(map.values(), 1); + + Material resultm1 = map.get(m1id); + + assertEquals(resultm1.getPermId(), m1id); + Material resultm2 = resultm1.getMaterialProperties().get("ANY_MATERIAL"); + assertEquals(resultm2.getPermId(), m2id); + Material resultm3 = resultm2.getMaterialProperties().get("ANY_MATERIAL"); + assertEquals(resultm1, resultm3); } // all potential error scenarios // create mateiral with mateiral properties + private MaterialCreation materialCreation(MaterialPermId permId) + { + String code = permId.getCode(); + String type = permId.getTypeCode(); + MaterialCreation materialCreation = new MaterialCreation(); + materialCreation.setCode(code); + materialCreation.setTypeId(new EntityTypePermId(type)); + materialCreation.setCreationId(new CreationId("creation " + code)); + materialCreation.setDescription("Material with code " + code); + return materialCreation; + } + private MaterialCreation geneCreation(String code) { MaterialCreation materialCreation = new MaterialCreation(); diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateMaterialTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateMaterialTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c480dce4f468cad0fd6d9747b9ee7727e749b42c --- /dev/null +++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateMaterialTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 ETH Zuerich, Scientific IT Services + * + * 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.ethz.sis.openbis.systemtest.api.v3; + +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Map; + +import org.testng.annotations.Test; + +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialUpdate; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialFetchOptions; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.material.IMaterialId; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.material.MaterialPermId; + +/** + * @author pkupczyk + */ +public class UpdateMaterialTest extends AbstractSampleTest +{ + + @Test + public void testSimpleMaterialUpdate() + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + MaterialUpdate update = new MaterialUpdate(); + update.setMaterialId(new MaterialPermId("1", "GENE")); + String description = "This is amodified gene description"; + update.setProperty("DESCRIPTION", description); + + v3api.updateMaterials(sessionToken, Arrays.asList(update)); + + MaterialFetchOptions fetchOptions = new MaterialFetchOptions(); + fetchOptions.withProperties(); + + Map<IMaterialId, Material> map = v3api.mapMaterials(sessionToken, Arrays.asList(update.getMaterialId()), fetchOptions); + + Material material = map.get(update.getMaterialId()); + assertEquals(material.getProperties().get("DESCRIPTION"), description); + } + +} diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateSampleTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateSampleTest.java index d00c37b6c12eecad2216605a6018a2e5c93bb625..5edb3f5703913f3055bbdc05fd6da5ea9ec04c33 100644 --- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateSampleTest.java +++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/api/v3/UpdateSampleTest.java @@ -32,6 +32,7 @@ import org.testng.annotations.Test; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.attachment.Attachment; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.attachment.AttachmentCreation; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.Sample; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.SampleCreation; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.SampleUpdate; @@ -41,6 +42,7 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.attachment.AttachmentFil import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.entitytype.EntityTypePermId; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.experiment.ExperimentPermId; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.experiment.IExperimentId; +import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.material.MaterialPermId; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.sample.ISampleId; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.sample.SampleIdentifier; import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.sample.SamplePermId; @@ -1241,4 +1243,34 @@ public class UpdateSampleTest extends AbstractSampleTest assertTags(sample2.getTags(), "/test/TEST_TAG_2", "/test/TEST_TAG_3"); } + @Test + public void testWithMaterialProperties() + { + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withMaterialProperties().withRegistrator(); + fetchOptions.withProperties(); + + SamplePermId permId = new SamplePermId("200902091219327-1025"); + + SampleUpdate update1 = new SampleUpdate(); + update1.setSampleId(permId); + update1.setProperty("ANY_MATERIAL", "BACTERIUM-X (BACTERIUM)"); + + v3api.updateSamples(sessionToken, Arrays.asList(update1)); + + Map<ISampleId, Sample> map = v3api.mapSamples(sessionToken, Arrays.asList(permId), fetchOptions); + + Sample sample = map.get(permId); + + assertEquals(sample.getProperties().get("BACTERIUM"), "BACTERIUM-X (BACTERIUM)"); + assertEquals(sample.getProperties().get("ANY_MATERIAL"), "BACTERIUM-X (BACTERIUM)"); + + Map<String, Material> materialProperties = sample.getMaterialProperties(); + + Material updatedBacterium = materialProperties.get("ANY_MATERIAL"); + assertEquals(updatedBacterium.getPermId(), new MaterialPermId("BACTERIUM-X", "BACTERIUM")); + } + }