| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 |
| * <p/> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p/> |
| * 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 org.apache.atlas.repository.store.graph.v1; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import org.apache.atlas.AtlasException; |
| import org.apache.atlas.RequestContextV1; |
| import org.apache.atlas.TestUtils; |
| import org.apache.atlas.TestUtilsV2; |
| import org.apache.atlas.exception.AtlasBaseException; |
| import org.apache.atlas.model.instance.AtlasEntity; |
| import org.apache.atlas.model.instance.AtlasEntityHeader; |
| import org.apache.atlas.model.instance.AtlasObjectId; |
| import org.apache.atlas.model.instance.AtlasStruct; |
| import org.apache.atlas.model.instance.EntityMutationResponse; |
| import org.apache.atlas.model.instance.EntityMutations; |
| import org.apache.atlas.model.typedef.AtlasClassificationDef; |
| import org.apache.atlas.model.typedef.AtlasEntityDef; |
| import org.apache.atlas.model.typedef.AtlasEnumDef; |
| import org.apache.atlas.model.typedef.AtlasStructDef; |
| import org.apache.atlas.model.typedef.AtlasTypesDef; |
| import org.apache.atlas.repository.Constants; |
| import org.apache.atlas.repository.graph.AtlasEdgeLabel; |
| import org.apache.atlas.repository.graph.AtlasGraphProvider; |
| import org.apache.atlas.repository.graph.GraphHelper; |
| import org.apache.atlas.repository.graphdb.AtlasGraph; |
| import org.apache.atlas.repository.graphdb.AtlasVertex; |
| import org.apache.atlas.repository.store.graph.AtlasEntityStore; |
| import org.apache.atlas.services.MetadataService; |
| import org.apache.atlas.store.AtlasTypeDefStore; |
| import org.apache.atlas.type.AtlasEntityType; |
| import org.apache.atlas.type.AtlasTypeRegistry; |
| import org.apache.atlas.type.AtlasTypeUtil; |
| import org.apache.atlas.typesystem.IReferenceableInstance; |
| import org.apache.atlas.typesystem.IStruct; |
| import org.apache.atlas.typesystem.ITypedReferenceableInstance; |
| import org.apache.atlas.typesystem.ITypedStruct; |
| import org.apache.atlas.typesystem.Struct; |
| import org.apache.atlas.typesystem.exception.EntityNotFoundException; |
| import org.apache.atlas.typesystem.persistence.Id; |
| import org.apache.atlas.typesystem.types.Multiplicity; |
| import org.apache.atlas.typesystem.types.TraitType; |
| import org.apache.atlas.typesystem.types.TypeSystem; |
| import org.testng.Assert; |
| import org.testng.annotations.AfterClass; |
| import org.testng.annotations.BeforeClass; |
| import org.testng.annotations.BeforeTest; |
| import org.testng.annotations.Test; |
| |
| import javax.inject.Inject; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import static org.apache.atlas.TestUtils.*; |
| import static org.mockito.Mockito.mock; |
| import static org.testng.Assert.assertEquals; |
| import static org.testng.Assert.assertNotEquals; |
| import static org.testng.Assert.assertTrue; |
| import static org.testng.Assert.fail; |
| |
| public abstract class AtlasDeleteHandlerV1Test { |
| |
| @Inject |
| AtlasTypeRegistry typeRegistry; |
| |
| @Inject |
| AtlasTypeDefStore typeDefStore; |
| |
| @Inject |
| AtlasEntityStore entityStore; |
| |
| @Inject |
| MetadataService metadataService; |
| |
| private AtlasEntityType compositeMapOwnerType; |
| |
| private AtlasEntityType compositeMapValueType; |
| |
| private TypeSystem typeSystem = TypeSystem.getInstance(); |
| |
| @BeforeClass |
| public void setUp() throws Exception { |
| metadataService = TestUtils.addSessionCleanupWrapper(metadataService); |
| final AtlasTypesDef deptTypesDef = TestUtilsV2.defineDeptEmployeeTypes(); |
| typeDefStore.createTypesDef(deptTypesDef); |
| |
| final AtlasTypesDef hiveTypesDef = TestUtilsV2.defineHiveTypes(); |
| typeDefStore.createTypesDef(hiveTypesDef); |
| |
| // Define type for map value. |
| AtlasEntityDef mapValueDef = AtlasTypeUtil.createClassTypeDef("CompositeMapValue", "CompositeMapValue" + "_description", "1.0", |
| ImmutableSet.<String>of(), |
| AtlasTypeUtil.createUniqueRequiredAttrDef("name", "string") |
| ); |
| |
| // Define type with map where the value is a composite class reference to MapValue. |
| AtlasEntityDef mapOwnerDef = AtlasTypeUtil.createClassTypeDef("CompositeMapOwner", "CompositeMapOwner_description", |
| ImmutableSet.<String>of(), |
| AtlasTypeUtil.createUniqueRequiredAttrDef("name", "string"), |
| new AtlasStructDef.AtlasAttributeDef("map", "map<string,CompositeMapValue>", true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, false, false, |
| new ArrayList<AtlasStructDef.AtlasConstraintDef>() {{ |
| add(new AtlasStructDef.AtlasConstraintDef(AtlasStructDef.AtlasConstraintDef.CONSTRAINT_TYPE_OWNED_REF)); |
| }}) |
| ); |
| |
| final AtlasTypesDef typesDef = AtlasTypeUtil.getTypesDef(ImmutableList.<AtlasEnumDef>of(), |
| ImmutableList.<AtlasStructDef>of(), |
| ImmutableList.<AtlasClassificationDef>of(), |
| ImmutableList.of(mapValueDef, mapOwnerDef)); |
| |
| typeDefStore.createTypesDef(typesDef); |
| |
| compositeMapOwnerType = typeRegistry.getEntityTypeByName("CompositeMapOwner"); |
| compositeMapValueType = typeRegistry.getEntityTypeByName("CompositeMapValue"); |
| } |
| |
| @BeforeTest |
| public void init() throws Exception { |
| RequestContextV1.clear(); |
| |
| } |
| |
| @AfterClass |
| public void clear() { |
| AtlasGraphProvider.cleanup(); |
| } |
| |
| @Test |
| public void testDeleteAndCreate() throws Exception { |
| init(); |
| final AtlasEntity dbEntity = TestUtilsV2.createDBEntity(); |
| EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(dbEntity), false); |
| |
| init(); |
| //delete entity should mark it as deleted |
| EntityMutationResponse deleteResponse = entityStore.deleteById(response.getFirstEntityCreated().getGuid()); |
| AtlasEntityHeader dbEntityCreated = response.getFirstEntityCreated(); |
| assertEquals(deleteResponse.getEntitiesByOperation(EntityMutations.EntityOperation.DELETE).get(0).getGuid(), dbEntityCreated.getGuid()); |
| |
| //get entity by unique attribute should throw EntityNotFoundException |
| try { |
| metadataService.getEntityDefinition(TestUtils.DATABASE_TYPE, "name", (String) response.getFirstEntityCreated().getAttribute("name")); |
| fail("Expected EntityNotFoundException"); |
| } catch(EntityNotFoundException e) { |
| //expected |
| } |
| |
| init(); |
| //Create the same entity again, should create new entity |
| AtlasEntity newDBEntity = TestUtilsV2.createDBEntity((String) dbEntity.getAttribute(NAME)); |
| EntityMutationResponse newCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(newDBEntity), false); |
| assertNotEquals(newCreationResponse.getFirstEntityCreated().getGuid(), response.getFirstEntityCreated().getGuid()); |
| |
| //get by unique attribute should return the new entity |
| ITypedReferenceableInstance instance = metadataService.getEntityDefinitionReference(TestUtils.DATABASE_TYPE, "name", (String) dbEntity.getAttribute("name")); |
| assertEquals(instance.getId()._getId(), newCreationResponse.getFirstEntityCreated().getGuid()); |
| } |
| |
| @Test |
| public void testDeleteReference() throws Exception { |
| //Deleting column should update table |
| final AtlasEntity dbEntity = TestUtilsV2.createDBEntity(); |
| |
| init(); |
| EntityMutationResponse dbCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(dbEntity), false); |
| |
| final AtlasEntity tableEntity = TestUtilsV2.createTableEntity(dbEntity); |
| final AtlasEntity columnEntity = TestUtilsV2.createColumnEntity(tableEntity); |
| tableEntity.setAttribute(COLUMNS_ATTR_NAME, Arrays.asList(AtlasTypeUtil.getAtlasObjectId(columnEntity))); |
| |
| AtlasEntity.AtlasEntityWithExtInfo input = new AtlasEntity.AtlasEntityWithExtInfo(tableEntity); |
| input.addReferredEntity(columnEntity); |
| |
| init(); |
| EntityMutationResponse tblCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(input), false); |
| final AtlasEntityHeader columnCreated = tblCreationResponse.getFirstCreatedEntityByTypeName(COLUMN_TYPE); |
| final AtlasEntityHeader tableCreated = tblCreationResponse.getFirstCreatedEntityByTypeName(TABLE_TYPE); |
| |
| init(); |
| EntityMutationResponse deletionResponse = entityStore.deleteById(columnCreated.getGuid()); |
| assertEquals(deletionResponse.getDeletedEntities().size(), 1); |
| assertEquals(deletionResponse.getDeletedEntities().get(0).getGuid(), columnCreated.getGuid()); |
| assertEquals(deletionResponse.getUpdatedEntities().size(), 1); |
| assertEquals(deletionResponse.getUpdatedEntities().get(0).getGuid(), tableCreated.getGuid()); |
| |
| assertEntityDeleted(columnCreated.getGuid()); |
| |
| assertColumnForTestDeleteReference(entityStore.getById(tableCreated.getGuid())); |
| |
| //Deleting table should update process |
| AtlasEntity process = TestUtilsV2.createProcessEntity(null, Arrays.asList(AtlasTypeUtil.getAtlasObjectId(tableCreated))); |
| init(); |
| final EntityMutationResponse processCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(process), false); |
| |
| init(); |
| entityStore.deleteById(tableCreated.getGuid()); |
| assertEntityDeleted(tableCreated.getGuid()); |
| |
| assertTableForTestDeleteReference(tableCreated.getGuid()); |
| assertProcessForTestDeleteReference(processCreationResponse.getFirstEntityCreated()); |
| } |
| |
| @Test |
| public void testDeleteEntities() throws Exception { |
| // Create a table entity, with 3 composite column entities |
| init(); |
| final AtlasEntity dbEntity = TestUtilsV2.createDBEntity(); |
| EntityMutationResponse dbCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(dbEntity), false); |
| |
| final AtlasEntity tableEntity = TestUtilsV2.createTableEntity(dbEntity); |
| AtlasEntity.AtlasEntitiesWithExtInfo entitiesInfo = new AtlasEntity.AtlasEntitiesWithExtInfo(tableEntity); |
| |
| final AtlasEntity columnEntity1 = TestUtilsV2.createColumnEntity(tableEntity); |
| entitiesInfo.addReferredEntity(columnEntity1); |
| final AtlasEntity columnEntity2 = TestUtilsV2.createColumnEntity(tableEntity); |
| entitiesInfo.addReferredEntity(columnEntity2); |
| final AtlasEntity columnEntity3 = TestUtilsV2.createColumnEntity(tableEntity); |
| entitiesInfo.addReferredEntity(columnEntity3); |
| |
| tableEntity.setAttribute(COLUMNS_ATTR_NAME, Arrays.asList(AtlasTypeUtil.getAtlasObjectId(columnEntity1), |
| AtlasTypeUtil.getAtlasObjectId(columnEntity2), |
| AtlasTypeUtil.getAtlasObjectId(columnEntity3))); |
| |
| init(); |
| |
| final EntityMutationResponse tblCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(entitiesInfo), false); |
| |
| final AtlasEntityHeader column1Created = tblCreationResponse.getCreatedEntityByTypeNameAndAttribute(COLUMN_TYPE, NAME, (String) columnEntity1.getAttribute(NAME)); |
| final AtlasEntityHeader column2Created = tblCreationResponse.getCreatedEntityByTypeNameAndAttribute(COLUMN_TYPE, NAME, (String) columnEntity2.getAttribute(NAME)); |
| final AtlasEntityHeader column3Created = tblCreationResponse.getCreatedEntityByTypeNameAndAttribute(COLUMN_TYPE, NAME, (String) columnEntity3.getAttribute(NAME)); |
| |
| // Retrieve the table entities from the Repository, to get their guids and the composite column guids. |
| ITypedReferenceableInstance tableInstance = metadataService.getEntityDefinitionReference(TestUtils.TABLE_TYPE, NAME, (String) tableEntity.getAttribute(NAME)); |
| List<IReferenceableInstance> columns = (List<IReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME); |
| |
| //Delete column |
| String colId = columns.get(0).getId()._getId(); |
| String tableId = tableInstance.getId()._getId(); |
| |
| init(); |
| |
| EntityMutationResponse deletionResponse = entityStore.deleteById(colId); |
| assertEquals(deletionResponse.getDeletedEntities().size(), 1); |
| assertEquals(deletionResponse.getDeletedEntities().get(0).getGuid(), colId); |
| assertEquals(deletionResponse.getUpdatedEntities().size(), 1); |
| assertEquals(deletionResponse.getUpdatedEntities().get(0).getGuid(), tableId); |
| assertEntityDeleted(colId); |
| |
| final AtlasEntity.AtlasEntityWithExtInfo tableEntityCreated = entityStore.getById(tableId); |
| assertDeletedColumn(tableEntityCreated); |
| |
| assertTestDisconnectUnidirectionalArrayReferenceFromClassType( |
| (List<AtlasObjectId>) tableEntityCreated.getEntity().getAttribute(COLUMNS_ATTR_NAME), colId); |
| |
| //update by removing a column - col1 |
| final AtlasEntity tableEntity1 = TestUtilsV2.createTableEntity(dbEntity, (String) tableEntity.getAttribute(NAME)); |
| |
| AtlasEntity.AtlasEntitiesWithExtInfo entitiesInfo1 = new AtlasEntity.AtlasEntitiesWithExtInfo(tableEntity1); |
| final AtlasEntity columnEntity3New = TestUtilsV2.createColumnEntity(tableEntity1, (String) column3Created.getAttribute(NAME)); |
| tableEntity1.setAttribute(COLUMNS_ATTR_NAME, Arrays.asList(AtlasTypeUtil.getAtlasObjectId(columnEntity3New))); |
| entitiesInfo1.addReferredEntity(columnEntity3New); |
| |
| init(); |
| deletionResponse = entityStore.createOrUpdate(new AtlasEntityStream(entitiesInfo1), false); |
| |
| assertEquals(deletionResponse.getDeletedEntities().size(), 1); |
| assertEquals(deletionResponse.getDeletedEntities().get(0).getGuid(), column2Created.getGuid()); |
| assertEntityDeleted(colId); |
| |
| // Delete the table entities. The deletion should cascade to their composite columns. |
| tableInstance = metadataService.getEntityDefinitionReference(TestUtils.TABLE_TYPE, NAME, (String) tableEntity.getAttribute(NAME)); |
| |
| init(); |
| EntityMutationResponse tblDeletionResponse = entityStore.deleteById(tableInstance.getId()._getId()); |
| assertEquals(tblDeletionResponse.getDeletedEntities().size(), 2); |
| |
| final AtlasEntityHeader tableDeleted = tblDeletionResponse.getFirstDeletedEntityByTypeName(TABLE_TYPE); |
| final AtlasEntityHeader colDeleted = tblDeletionResponse.getFirstDeletedEntityByTypeName(COLUMN_TYPE); |
| |
| // Verify that deleteEntities() response has guids for tables and their composite columns. |
| Assert.assertTrue(tableDeleted.getGuid().equals(tableInstance.getId()._getId())); |
| Assert.assertTrue(colDeleted.getGuid().equals(column3Created.getGuid())); |
| |
| // Verify that tables and their composite columns have been deleted from the graph Repository. |
| assertEntityDeleted(tableDeleted.getGuid()); |
| assertEntityDeleted(colDeleted.getGuid()); |
| |
| } |
| |
| protected abstract void assertDeletedColumn(AtlasEntity.AtlasEntityWithExtInfo tableInstance) throws AtlasException, AtlasBaseException; |
| |
| protected abstract void assertTestDeleteEntities(AtlasEntity.AtlasEntityWithExtInfo tableInstance) throws Exception; |
| |
| protected abstract void assertTableForTestDeleteReference(String tableId) throws Exception; |
| |
| protected abstract void assertColumnForTestDeleteReference(AtlasEntity.AtlasEntityWithExtInfo tableInstance) |
| throws AtlasBaseException; |
| |
| protected abstract void assertProcessForTestDeleteReference(AtlasEntityHeader processInstance) throws Exception; |
| |
| protected abstract void assertEntityDeleted(String id) throws Exception; |
| |
| String getFirstGuid(Map<String, AtlasEntity> entityMap) { |
| return entityMap.keySet().iterator().next(); |
| } |
| |
| @Test |
| public void testUpdateEntity_MultiplicityOneNonCompositeReference() throws Exception { |
| AtlasEntity.AtlasEntitiesWithExtInfo hrDept = TestUtilsV2.createDeptEg2(); |
| init(); |
| |
| RequestContextV1.clear(); |
| final EntityMutationResponse hrDeptCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(hrDept), false); |
| final AtlasEntityHeader deptCreated = hrDeptCreationResponse.getFirstUpdatedEntityByTypeName(DEPARTMENT_TYPE); |
| final AtlasEntityHeader maxEmployeeCreated = hrDeptCreationResponse.getCreatedEntityByTypeNameAndAttribute(TestUtilsV2.EMPLOYEE_TYPE, NAME, "Max"); |
| final AtlasEntityHeader johnEmployeeCreated = hrDeptCreationResponse.getUpdatedEntityByTypeNameAndAttribute(TestUtilsV2.EMPLOYEE_TYPE, NAME, "John"); |
| final AtlasEntityHeader janeEmployeeCreated = hrDeptCreationResponse.getCreatedEntityByTypeNameAndAttribute(TestUtilsV2.MANAGER_TYPE, NAME, "Jane"); |
| final AtlasEntityHeader juliusEmployeeCreated = hrDeptCreationResponse.getUpdatedEntityByTypeNameAndAttribute(TestUtilsV2.MANAGER_TYPE, NAME, "Julius"); |
| |
| ITypedReferenceableInstance max = metadataService.getEntityDefinition(maxEmployeeCreated.getGuid()); |
| String maxGuid = max.getId()._getId(); |
| AtlasVertex vertex = GraphHelper.getInstance().getVertexForGUID(maxGuid); |
| Long creationTimestamp = GraphHelper.getSingleValuedProperty(vertex, Constants.TIMESTAMP_PROPERTY_KEY, Long.class); |
| Assert.assertNotNull(creationTimestamp); |
| |
| Long modificationTimestampPreUpdate = GraphHelper.getSingleValuedProperty(vertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, Long.class); |
| Assert.assertNotNull(modificationTimestampPreUpdate); |
| |
| AtlasEntity maxEmployee = getEmployeeByName(hrDept, "Max"); |
| maxEmployee.setAttribute("mentor", AtlasTypeUtil.getAtlasObjectId(johnEmployeeCreated)); |
| maxEmployee.setAttribute("department", AtlasTypeUtil.getAtlasObjectId(deptCreated)); |
| maxEmployee.setAttribute("manager", AtlasTypeUtil.getAtlasObjectId(janeEmployeeCreated)); |
| |
| init(); |
| EntityMutationResponse entityResult = entityStore.createOrUpdate(new AtlasEntityStream(maxEmployee), false); |
| |
| assertEquals(entityResult.getUpdatedEntities().size(), 1); |
| assertTrue(extractGuids(entityResult.getUpdatedEntities()).contains(maxGuid)); |
| |
| // Verify the update was applied correctly - john should now be max's mentor. |
| max = metadataService.getEntityDefinition(maxGuid); |
| ITypedReferenceableInstance refTarget = (ITypedReferenceableInstance) max.get("mentor"); |
| Assert.assertEquals(refTarget.getId()._getId(), johnEmployeeCreated.getGuid()); |
| |
| // Verify modification timestamp was updated. |
| vertex = GraphHelper.getInstance().getVertexForGUID(maxGuid); |
| Long modificationTimestampPostUpdate = GraphHelper.getSingleValuedProperty(vertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, Long.class); |
| Assert.assertNotNull(modificationTimestampPostUpdate); |
| Assert.assertTrue(creationTimestamp < modificationTimestampPostUpdate); |
| |
| // Update max's mentor reference to jane. |
| maxEmployee.setAttribute("mentor", AtlasTypeUtil.getAtlasObjectId(janeEmployeeCreated)); |
| init(); |
| entityResult = entityStore.createOrUpdate(new AtlasEntityStream(maxEmployee), false); |
| assertEquals(entityResult.getUpdatedEntities().size(), 1); |
| assertTrue(extractGuids(entityResult.getUpdatedEntities()).contains(maxGuid)); |
| |
| // Verify the update was applied correctly - jane should now be max's mentor. |
| max = metadataService.getEntityDefinition(maxGuid); |
| refTarget = (ITypedReferenceableInstance) max.get("mentor"); |
| Assert.assertEquals(refTarget.getId()._getId(), janeEmployeeCreated.getGuid()); |
| |
| // Verify modification timestamp was updated. |
| vertex = GraphHelper.getInstance().getVertexForGUID(maxGuid); |
| Long modificationTimestampPost2ndUpdate = GraphHelper.getSingleValuedProperty(vertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, Long.class); |
| Assert.assertNotNull(modificationTimestampPost2ndUpdate); |
| Assert.assertTrue(modificationTimestampPostUpdate < modificationTimestampPost2ndUpdate); |
| |
| ITypedReferenceableInstance julius = metadataService.getEntityDefinition(juliusEmployeeCreated.getGuid()); |
| Id juliusId = julius.getId(); |
| |
| init(); |
| maxEmployee.setAttribute("manager", AtlasTypeUtil.getAtlasObjectId(juliusEmployeeCreated)); |
| entityResult = entityStore.createOrUpdate(new AtlasEntityStream(maxEmployee), false); |
| assertEquals(entityResult.getUpdatedEntities().size(), 3); |
| List<String> updatedGuids = extractGuids(entityResult.getUpdatedEntities()); |
| assertTrue(updatedGuids.contains(maxGuid)); |
| assertTrue(updatedGuids.contains(janeEmployeeCreated.getGuid())); |
| // Should have updated julius to add max in subordinates list. |
| assertTrue(updatedGuids.contains(juliusEmployeeCreated.getGuid())); |
| |
| // Verify the update was applied correctly - julius should now be max's manager and max should be julius' subordinate. |
| max = metadataService.getEntityDefinition(maxGuid); |
| refTarget = (ITypedReferenceableInstance) max.get("manager"); |
| Assert.assertEquals(refTarget.getId()._getId(), juliusId._getId()); |
| julius = metadataService.getEntityDefinition(juliusId._getId()); |
| Object value = julius.get("subordinates"); |
| Assert.assertTrue(value instanceof List); |
| List<ITypedReferenceableInstance> refList = (List<ITypedReferenceableInstance>) value; |
| Assert.assertEquals(refList.size(), 1); |
| Assert.assertEquals(refList.get(0).getId()._getId(), maxGuid); |
| |
| assertTestUpdateEntity_MultiplicityOneNonCompositeReference(janeEmployeeCreated.getGuid()); |
| } |
| |
| private Map<String, String> getEmployeeNameGuidMap(final ITypedReferenceableInstance hrDept) throws AtlasException { |
| Object refValue = hrDept.get("employees"); |
| Assert.assertTrue(refValue instanceof List); |
| List<Object> employees = (List<Object>)refValue; |
| Assert.assertEquals(employees.size(), 4); |
| Map<String, String> nameGuidMap = new HashMap<String, String>() {{ |
| put("hr", hrDept.getId()._getId()); |
| }}; |
| |
| for (Object listValue : employees) { |
| Assert.assertTrue(listValue instanceof ITypedReferenceableInstance); |
| ITypedReferenceableInstance employee = (ITypedReferenceableInstance) listValue; |
| nameGuidMap.put((String)employee.get("name"), employee.getId()._getId()); |
| } |
| return nameGuidMap; |
| } |
| |
| |
| private AtlasEntity getEmployeeByName(AtlasEntity.AtlasEntitiesWithExtInfo hrDept, String name) { |
| for (AtlasEntity entity : hrDept.getEntities()) { |
| if ( name.equals(entity.getAttribute(NAME))) { |
| return entity; |
| } |
| } |
| return null; |
| } |
| |
| protected abstract void assertTestUpdateEntity_MultiplicityOneNonCompositeReference(String janeGuid) throws Exception; |
| |
| /** |
| * Verify deleting an entity which is contained by another |
| * entity through a bi-directional composite reference. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testDisconnectBidirectionalReferences() throws Exception { |
| AtlasEntity.AtlasEntitiesWithExtInfo hrDept = TestUtilsV2.createDeptEg2(); |
| init(); |
| final EntityMutationResponse hrDeptCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(hrDept), false); |
| |
| final AtlasEntityHeader deptCreated = hrDeptCreationResponse.getFirstCreatedEntityByTypeName(DEPARTMENT_TYPE); |
| final AtlasEntityHeader maxEmployee = hrDeptCreationResponse.getCreatedEntityByTypeNameAndAttribute(TestUtilsV2.EMPLOYEE_TYPE, NAME, "Max"); |
| final AtlasEntityHeader johnEmployee = hrDeptCreationResponse.getCreatedEntityByTypeNameAndAttribute(TestUtilsV2.EMPLOYEE_TYPE, NAME, "John"); |
| final AtlasEntityHeader janeEmployee = hrDeptCreationResponse.getCreatedEntityByTypeNameAndAttribute(TestUtilsV2.MANAGER_TYPE, NAME, "Jane"); |
| final AtlasEntityHeader juliusEmployee = hrDeptCreationResponse.getCreatedEntityByTypeNameAndAttribute(TestUtilsV2.MANAGER_TYPE, NAME, "Julius"); |
| |
| ITypedReferenceableInstance hrDeptInstance = metadataService.getEntityDefinition(deptCreated.getGuid()); |
| Map<String, String> nameGuidMap = getEmployeeNameGuidMap(hrDeptInstance); |
| |
| // Verify that Max is one of Jane's subordinates. |
| ITypedReferenceableInstance jane = metadataService.getEntityDefinition(janeEmployee.getGuid()); |
| Object refValue = jane.get("subordinates"); |
| Assert.assertTrue(refValue instanceof List); |
| List<Object> subordinates = (List<Object>)refValue; |
| Assert.assertEquals(subordinates.size(), 2); |
| List<String> subordinateIds = new ArrayList<>(2); |
| for (Object listValue : subordinates) { |
| Assert.assertTrue(listValue instanceof ITypedReferenceableInstance); |
| ITypedReferenceableInstance employee = (ITypedReferenceableInstance) listValue; |
| subordinateIds.add(employee.getId()._getId()); |
| } |
| Assert.assertTrue(subordinateIds.contains(maxEmployee.getGuid())); |
| |
| init(); |
| EntityMutationResponse entityResult = entityStore.deleteById(maxEmployee.getGuid()); |
| ITypedReferenceableInstance john = metadataService.getEntityDefinitionReference(TestUtilsV2.EMPLOYEE_TYPE, NAME, "John"); |
| |
| assertEquals(entityResult.getDeletedEntities().size(), 1); |
| assertEquals(entityResult.getDeletedEntities().get(0).getGuid(), maxEmployee.getGuid()); |
| assertEquals(entityResult.getUpdatedEntities().size(), 3); |
| |
| assertEquals(extractGuids(entityResult.getUpdatedEntities()), Arrays.asList(janeEmployee.getGuid(), deptCreated.getGuid(), johnEmployee.getGuid())); |
| assertEntityDeleted(maxEmployee.getGuid()); |
| |
| assertMaxForTestDisconnectBidirectionalReferences(nameGuidMap); |
| |
| // Now delete jane - this should disconnect the manager reference from her |
| // subordinate. |
| init(); |
| entityResult = entityStore.deleteById(janeEmployee.getGuid()); |
| assertEquals(entityResult.getDeletedEntities().size(), 1); |
| assertEquals(entityResult.getDeletedEntities().get(0).getGuid(), janeEmployee.getGuid()); |
| assertEquals(entityResult.getUpdatedEntities().size(), 2); |
| assertEquals(extractGuids(entityResult.getUpdatedEntities()), Arrays.asList(deptCreated.getGuid(), johnEmployee.getGuid())); |
| |
| assertEntityDeleted(janeEmployee.getGuid()); |
| |
| final AtlasEntity.AtlasEntityWithExtInfo johnUpdated = entityStore.getById(johnEmployee.getGuid()); |
| assertJohnForTestDisconnectBidirectionalReferences(johnUpdated, janeEmployee.getGuid()); |
| } |
| |
| protected List<String> extractGuids(final List<AtlasEntityHeader> updatedEntities) { |
| List<String> guids = new ArrayList<>(); |
| for (AtlasEntityHeader header : updatedEntities ) { |
| guids.add(header.getGuid()); |
| } |
| return guids; |
| } |
| |
| protected abstract void assertJohnForTestDisconnectBidirectionalReferences(AtlasEntity.AtlasEntityWithExtInfo john, |
| String janeGuid) throws Exception; |
| |
| protected abstract void assertMaxForTestDisconnectBidirectionalReferences(Map<String, String> nameGuidMap) |
| throws Exception; |
| |
| protected abstract void assertTestDisconnectUnidirectionalArrayReferenceFromClassType( |
| List<AtlasObjectId> columns, String columnGuid) throws AtlasBaseException; |
| |
| /** |
| * Verify deleting entities that are the target of a unidirectional class array reference |
| * from a struct or trait instance. |
| */ |
| @Test |
| public void testDisconnectUnidirectionalArrayReferenceFromStructAndTraitTypes() throws Exception { |
| // Define class types. |
| AtlasStructDef.AtlasAttributeDef[] structTargetAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("attr1", "string", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList())}; |
| |
| AtlasEntityDef structTargetDef = |
| new AtlasEntityDef("StructTarget", "StructTarget_description", "1.0", |
| Arrays.asList(structTargetAttributes), Collections.<String>emptySet()); |
| |
| |
| AtlasStructDef.AtlasAttributeDef[] traitTargetAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("attr1", "string", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList())}; |
| |
| AtlasEntityDef traitTargetDef = |
| new AtlasEntityDef("TraitTarget", "TraitTarget_description", "1.0", |
| Arrays.asList(traitTargetAttributes), Collections.<String>emptySet()); |
| |
| AtlasStructDef.AtlasAttributeDef[] structContainerAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("struct", "TestStruct", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList())}; |
| |
| AtlasEntityDef structContainerDef = |
| new AtlasEntityDef("StructContainer", "StructContainer_description", "1.0", |
| Arrays.asList(structContainerAttributes), Collections.<String>emptySet()); |
| |
| // Define struct and trait types which have a unidirectional array reference |
| // to a class type. |
| AtlasStructDef.AtlasAttributeDef[] structDefAttributes = new AtlasStructDef.AtlasAttributeDef[] { |
| new AtlasStructDef.AtlasAttributeDef("target", "array<StructTarget>", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()), |
| |
| new AtlasStructDef.AtlasAttributeDef("nestedStructs", "array<NestedStruct>", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()) }; |
| |
| AtlasStructDef structDef = new AtlasStructDef("TestStruct", "TestStruct_desc", "1.0", Arrays.asList(structDefAttributes)); |
| |
| |
| // Define struct and trait types which have a unidirectional array reference |
| // to a class type. |
| AtlasStructDef.AtlasAttributeDef[] nestedStructDefAttributes = new AtlasStructDef.AtlasAttributeDef[] { |
| new AtlasStructDef.AtlasAttributeDef("attr1", "string", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()), |
| |
| new AtlasStructDef.AtlasAttributeDef("target", "array<TraitTarget>", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()) }; |
| |
| AtlasStructDef nestedStructDef = new AtlasStructDef("NestedStruct", "NestedStruct_desc", "1.0", Arrays.asList(nestedStructDefAttributes)); |
| |
| AtlasStructDef.AtlasAttributeDef[] traitDefAttributes = new AtlasStructDef.AtlasAttributeDef[] { |
| new AtlasStructDef.AtlasAttributeDef("target", "array<TraitTarget>", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()) |
| }; |
| |
| AtlasClassificationDef traitDef = new AtlasClassificationDef("TestTrait", "TestTrait_desc", "1.0", Arrays.asList(traitDefAttributes)); |
| |
| AtlasTypesDef typesDef = AtlasTypeUtil.getTypesDef(ImmutableList.<AtlasEnumDef>of(), |
| ImmutableList.<AtlasStructDef>of(structDef, nestedStructDef), |
| ImmutableList.<AtlasClassificationDef>of(traitDef), |
| ImmutableList.<AtlasEntityDef>of(structTargetDef, traitTargetDef, structContainerDef)); |
| |
| typeDefStore.createTypesDef(typesDef); |
| |
| // Create instances of class, struct, and trait types. |
| final AtlasEntity structTargetEntity = new AtlasEntity("StructTarget"); |
| final AtlasEntity traitTargetEntity = new AtlasEntity("TraitTarget"); |
| final AtlasEntity structContainerEntity = new AtlasEntity("StructContainer"); |
| AtlasStruct structInstance = new AtlasStruct("TestStruct"); |
| AtlasStruct nestedStructInstance = new AtlasStruct("NestedStruct"); |
| Struct traitInstance = new Struct("TestTrait"); |
| structContainerEntity.setAttribute("struct", structInstance); |
| structInstance.setAttribute("target", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(structTargetEntity))); |
| structInstance.setAttribute("nestedStructs", ImmutableList.of(nestedStructInstance)); |
| |
| AtlasEntity.AtlasEntitiesWithExtInfo structCreationObj = new AtlasEntity.AtlasEntitiesWithExtInfo(); |
| structCreationObj.addEntity(structContainerEntity); |
| structCreationObj.addEntity(traitTargetEntity); |
| structCreationObj.addReferredEntity(structTargetEntity); |
| |
| init(); |
| |
| AtlasEntityStream entityStream = new AtlasEntityStream(structCreationObj); |
| |
| EntityMutationResponse response = entityStore.createOrUpdate(entityStream, false); |
| Assert.assertEquals(response.getCreatedEntities().size(), 3); |
| |
| final List<String> structTarget = metadataService.getEntityList("StructTarget"); |
| Assert.assertEquals(structTarget.size(), 1); |
| final String structTargetGuid = structTarget.get(0); |
| |
| final List<String> traitTarget = metadataService.getEntityList("TraitTarget"); |
| Assert.assertEquals(traitTarget.size(), 1); |
| final String traitTargetGuid = traitTarget.get(0); |
| |
| final List<String> structContainerTarget = metadataService.getEntityList("StructContainer"); |
| Assert.assertEquals(structContainerTarget.size(), 1); |
| String structContainerGuid = structContainerTarget.get(0); |
| |
| // Add TestTrait to StructContainer instance |
| traitInstance.set("target", ImmutableList.of(new Id(traitTargetGuid, 0, "TraitTarget"))); |
| TraitType traitType = typeSystem.getDataType(TraitType.class, "TestTrait"); |
| ITypedStruct convertedTrait = traitType.convert(traitInstance, Multiplicity.REQUIRED); |
| metadataService.addTrait(structContainerGuid, convertedTrait); |
| |
| // Verify that the unidirectional references from the struct and trait instances |
| // are pointing at the target entities. |
| final ITypedReferenceableInstance structContainerConvertedEntity = metadataService.getEntityDefinition(structContainerGuid); |
| Object object = structContainerConvertedEntity.get("struct"); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof ITypedStruct); |
| ITypedStruct struct = (ITypedStruct) object; |
| object = struct.get("target"); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof List); |
| List<ITypedReferenceableInstance> refList = (List<ITypedReferenceableInstance>)object; |
| Assert.assertEquals(refList.size(), 1); |
| Assert.assertEquals(refList.get(0).getId()._getId(), structTargetGuid); |
| |
| IStruct trait = structContainerConvertedEntity.getTrait("TestTrait"); |
| Assert.assertNotNull(trait); |
| object = trait.get("target"); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof List); |
| refList = (List<ITypedReferenceableInstance>)object; |
| Assert.assertEquals(refList.size(), 1); |
| Assert.assertEquals(refList.get(0).getId()._getId(), traitTargetGuid); |
| |
| init(); |
| // Delete the entities that are targets of the struct and trait instances. |
| EntityMutationResponse entityResult = entityStore.deleteByIds(new ArrayList<String>() {{ |
| add(structTargetGuid); |
| add(traitTargetGuid); |
| }}); |
| Assert.assertEquals(entityResult.getDeletedEntities().size(), 2); |
| Assert.assertTrue(extractGuids(entityResult.getDeletedEntities()).containsAll(Arrays.asList(structTargetGuid, traitTargetGuid))); |
| assertEntityDeleted(structTargetGuid); |
| assertEntityDeleted(traitTargetGuid); |
| |
| assertTestDisconnectUnidirectionalArrayReferenceFromStructAndTraitTypes(structContainerGuid); |
| |
| init(); |
| // Delete the entity which contains nested structs and has the TestTrait trait. |
| entityResult = entityStore.deleteById(structContainerGuid); |
| Assert.assertEquals(entityResult.getDeletedEntities().size(), 1); |
| Assert.assertTrue(extractGuids(entityResult.getDeletedEntities()).contains(structContainerGuid)); |
| assertEntityDeleted(structContainerGuid); |
| |
| // Verify all TestStruct struct vertices were removed. |
| assertVerticesDeleted(getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "TestStruct")); |
| |
| // Verify all NestedStruct struct vertices were removed. |
| assertVerticesDeleted(getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "NestedStruct")); |
| |
| // Verify all TestTrait trait vertices were removed. |
| assertVerticesDeleted(getVertices(Constants.ENTITY_TYPE_PROPERTY_KEY, "TestTrait")); |
| } |
| |
| |
| /** |
| * Verify deleting entities that are the target of class map references. |
| */ |
| @Test |
| public void testDisconnectMapReferenceFromClassType() throws Exception { |
| // Define type for map value. |
| AtlasStructDef.AtlasAttributeDef[] mapValueAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("biMapOwner", "MapOwner", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| new ArrayList<AtlasStructDef.AtlasConstraintDef>() {{ |
| add(new AtlasStructDef.AtlasConstraintDef( |
| AtlasStructDef.AtlasConstraintDef.CONSTRAINT_TYPE_INVERSE_REF, new HashMap<String, Object>() {{ |
| put(AtlasStructDef.AtlasConstraintDef.CONSTRAINT_PARAM_ATTRIBUTE, "biMap"); |
| }})); |
| }})}; |
| |
| AtlasEntityDef mapValueContainerDef = |
| new AtlasEntityDef("MapValue", "MapValue_desc", "1.0", |
| Arrays.asList(mapValueAttributes), Collections.<String>emptySet()); |
| |
| // Define type with unidirectional and bidirectional map references, |
| // where the map value is a class reference to MapValue. |
| |
| AtlasStructDef.AtlasAttributeDef[] mapOwnerAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("map", "map<string,MapValue>", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()), |
| new AtlasStructDef.AtlasAttributeDef("biMap", "map<string,MapValue>", |
| true, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 0, 1, |
| false, false, |
| new ArrayList<AtlasStructDef.AtlasConstraintDef>() {{ |
| add(new AtlasStructDef.AtlasConstraintDef( |
| AtlasStructDef.AtlasConstraintDef.CONSTRAINT_TYPE_INVERSE_REF, new HashMap<String, Object>() {{ |
| put(AtlasStructDef.AtlasConstraintDef.CONSTRAINT_PARAM_ATTRIBUTE, "biMapOwner"); |
| }})); |
| }})}; |
| |
| AtlasEntityDef mapOwnerContainerDef = |
| new AtlasEntityDef("MapOwner", "MapOwner_desc", "1.0", |
| Arrays.asList(mapOwnerAttributes), Collections.<String>emptySet()); |
| |
| AtlasTypesDef typesDef = AtlasTypeUtil.getTypesDef(ImmutableList.<AtlasEnumDef>of(), |
| ImmutableList.<AtlasStructDef>of(), |
| ImmutableList.<AtlasClassificationDef>of(), |
| ImmutableList.<AtlasEntityDef>of(mapValueContainerDef, mapOwnerContainerDef)); |
| |
| typeDefStore.createTypesDef(typesDef); |
| |
| // Create instances of MapOwner and MapValue. |
| // Set MapOwner.map and MapOwner.biMap with one entry that references MapValue instance. |
| AtlasEntity mapOwnerInstance = new AtlasEntity("MapOwner"); |
| AtlasEntity mapValueInstance = new AtlasEntity("MapValue"); |
| |
| mapOwnerInstance.setAttribute("map", Collections.singletonMap("value1", AtlasTypeUtil.getAtlasObjectId(mapValueInstance))); |
| mapOwnerInstance.setAttribute("biMap", Collections.singletonMap("value1", AtlasTypeUtil.getAtlasObjectId(mapValueInstance))); |
| // Set biMapOwner reverse reference on MapValue. |
| mapValueInstance.setAttribute("biMapOwner", AtlasTypeUtil.getAtlasObjectId(mapOwnerInstance)); |
| |
| AtlasEntity.AtlasEntitiesWithExtInfo entities = new AtlasEntity.AtlasEntitiesWithExtInfo(); |
| entities.addReferredEntity(mapValueInstance); |
| entities.addEntity(mapOwnerInstance); |
| |
| final EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(entities), false); |
| Assert.assertEquals(response.getCreatedEntities().size(), 2); |
| final List<AtlasEntityHeader> mapOwnerCreated = response.getCreatedEntitiesByTypeName("MapOwner"); |
| AtlasEntity.AtlasEntityWithExtInfo mapOwnerEntity = entityStore.getById(mapOwnerCreated.get(0).getGuid()); |
| |
| String edgeLabel = AtlasGraphUtilsV1.getAttributeEdgeLabel(typeRegistry.getEntityTypeByName("MapOwner"), "map"); |
| String mapEntryLabel = edgeLabel + "." + "value1"; |
| AtlasEdgeLabel atlasEdgeLabel = new AtlasEdgeLabel(mapEntryLabel); |
| |
| // Verify MapOwner.map attribute has expected value. |
| String mapValueGuid = null; |
| AtlasVertex mapOwnerVertex = null; |
| for (String mapAttrName : Arrays.asList("map", "biMap")) { |
| Object object = mapOwnerEntity.getEntity().getAttribute(mapAttrName); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof Map); |
| Map<String, AtlasObjectId> map = (Map<String, AtlasObjectId>)object; |
| Assert.assertEquals(map.size(), 1); |
| AtlasObjectId value1Id = map.get("value1"); |
| Assert.assertNotNull(value1Id); |
| mapValueGuid = value1Id.getGuid(); |
| mapOwnerVertex = GraphHelper.getInstance().getVertexForGUID(mapOwnerEntity.getEntity().getGuid()); |
| object = mapOwnerVertex.getProperty(atlasEdgeLabel.getQualifiedMapKey(), Object.class); |
| Assert.assertNotNull(object); |
| } |
| |
| // Delete the map value instance. |
| // This should disconnect the references from the map owner instance. |
| entityStore.deleteById(mapValueGuid); |
| assertEntityDeleted(mapValueGuid); |
| assertTestDisconnectMapReferenceFromClassType(mapOwnerEntity.getEntity().getGuid()); |
| } |
| |
| protected abstract void assertTestDisconnectMapReferenceFromClassType(String mapOwnerGuid) throws Exception; |
| |
| @Test |
| public void testDeleteByUniqueAttribute() throws Exception { |
| // Create a table entity, with 3 composite column entities |
| init(); |
| final AtlasEntity dbEntity = TestUtilsV2.createDBEntity(); |
| EntityMutationResponse dbCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(dbEntity), false); |
| |
| final AtlasEntity tableEntity = TestUtilsV2.createTableEntity(dbEntity); |
| AtlasEntity.AtlasEntitiesWithExtInfo entitiesInfo = new AtlasEntity.AtlasEntitiesWithExtInfo(tableEntity); |
| |
| final AtlasEntity columnEntity1 = TestUtilsV2.createColumnEntity(tableEntity); |
| entitiesInfo.addReferredEntity(columnEntity1); |
| final AtlasEntity columnEntity2 = TestUtilsV2.createColumnEntity(tableEntity); |
| entitiesInfo.addReferredEntity(columnEntity2); |
| final AtlasEntity columnEntity3 = TestUtilsV2.createColumnEntity(tableEntity); |
| entitiesInfo.addReferredEntity(columnEntity3); |
| |
| tableEntity.setAttribute(COLUMNS_ATTR_NAME, Arrays.asList(AtlasTypeUtil.getAtlasObjectId(columnEntity1), |
| AtlasTypeUtil.getAtlasObjectId(columnEntity2), |
| AtlasTypeUtil.getAtlasObjectId(columnEntity3))); |
| |
| init(); |
| |
| final EntityMutationResponse tblCreationResponse = entityStore.createOrUpdate(new AtlasEntityStream(entitiesInfo), false); |
| |
| final AtlasEntityHeader column1Created = tblCreationResponse.getCreatedEntityByTypeNameAndAttribute(COLUMN_TYPE, NAME, (String) columnEntity1.getAttribute(NAME)); |
| final AtlasEntityHeader column2Created = tblCreationResponse.getCreatedEntityByTypeNameAndAttribute(COLUMN_TYPE, NAME, (String) columnEntity2.getAttribute(NAME)); |
| final AtlasEntityHeader column3Created = tblCreationResponse.getCreatedEntityByTypeNameAndAttribute(COLUMN_TYPE, NAME, (String) columnEntity3.getAttribute(NAME)); |
| |
| // Retrieve the table entities from the Repository, to get their guids and the composite column guids. |
| ITypedReferenceableInstance tableInstance = metadataService.getEntityDefinitionReference(TestUtils.TABLE_TYPE, NAME, (String) tableEntity.getAttribute(NAME)); |
| List<IReferenceableInstance> columns = (List<IReferenceableInstance>) tableInstance.get(COLUMNS_ATTR_NAME); |
| |
| //Delete column |
| String colId = columns.get(0).getId()._getId(); |
| String tableId = tableInstance.getId()._getId(); |
| |
| init(); |
| |
| Map<String, Object> uniqueAttrs = new HashMap<>(); |
| uniqueAttrs.put(NAME, column1Created.getAttribute(NAME)); |
| |
| AtlasEntityType columnType = typeRegistry.getEntityTypeByName(COLUMN_TYPE); |
| EntityMutationResponse deletionResponse = entityStore.deleteByUniqueAttributes(columnType, uniqueAttrs); |
| assertEquals(deletionResponse.getDeletedEntities().size(), 1); |
| assertEquals(deletionResponse.getDeletedEntities().get(0).getGuid(), colId); |
| assertEquals(deletionResponse.getUpdatedEntities().size(), 1); |
| assertEquals(deletionResponse.getUpdatedEntities().get(0).getGuid(), tableId); |
| assertEntityDeleted(colId); |
| } |
| |
| @Test |
| public void testDeleteEntitiesWithCompositeMapReference() throws Exception { |
| // Create instances of MapOwner and MapValue. |
| // Set MapOwner.map with one entry that references MapValue instance. |
| AtlasEntity.AtlasEntityWithExtInfo entityDefinition = createMapOwnerAndValueEntities(); |
| String mapOwnerGuid = entityDefinition.getEntity().getGuid(); |
| |
| // Verify MapOwner.map attribute has expected value. |
| AtlasEntity.AtlasEntityWithExtInfo mapOwnerInstance = entityStore.getById(mapOwnerGuid); |
| Object object = mapOwnerInstance.getEntity().getAttribute("map"); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof Map); |
| Map<String, AtlasObjectId> map = (Map<String, AtlasObjectId>)object; |
| Assert.assertEquals(map.size(), 1); |
| AtlasObjectId mapValueInstance = map.get("value1"); |
| Assert.assertNotNull(mapValueInstance); |
| String mapValueGuid = mapValueInstance.getGuid(); |
| String edgeLabel = AtlasGraphUtilsV1.getAttributeEdgeLabel(compositeMapOwnerType, "map"); |
| String mapEntryLabel = edgeLabel + "." + "value1"; |
| AtlasEdgeLabel atlasEdgeLabel = new AtlasEdgeLabel(mapEntryLabel); |
| AtlasVertex mapOwnerVertex = GraphHelper.getInstance().getVertexForGUID(mapOwnerGuid); |
| object = mapOwnerVertex.getProperty(atlasEdgeLabel.getQualifiedMapKey(), Object.class); |
| Assert.assertNotNull(object); |
| |
| RequestContextV1.clear(); |
| List<AtlasEntityHeader> deletedEntities = entityStore.deleteById(mapOwnerGuid).getDeletedEntities(); |
| Assert.assertEquals(deletedEntities.size(), 2); |
| Assert.assertTrue(extractGuids(deletedEntities).contains(mapOwnerGuid)); |
| Assert.assertTrue(extractGuids(deletedEntities).contains(mapValueGuid)); |
| |
| assertEntityDeleted(mapOwnerGuid); |
| assertEntityDeleted(mapValueGuid); |
| } |
| |
| @Test |
| public void testDeleteTargetOfRequiredMapReference() throws Exception { |
| // Define type for map value. |
| AtlasEntityDef mapValueDef = |
| new AtlasEntityDef("RequiredMapValue", "RequiredMapValue_description", "1.0", |
| Collections.<AtlasStructDef.AtlasAttributeDef>emptyList(), Collections.<String>emptySet()); |
| |
| AtlasStructDef.AtlasAttributeDef[] mapOwnerAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("map", "map<string,RequiredMapValue>", |
| false, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 1, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()) |
| }; |
| |
| AtlasEntityDef mapOwnerDef = |
| new AtlasEntityDef("RequiredMapOwner", "RequiredMapOwner_description", "1.0", |
| Arrays.asList(mapOwnerAttributes), Collections.<String>emptySet()); |
| |
| AtlasTypesDef typesDef = AtlasTypeUtil.getTypesDef(ImmutableList.<AtlasEnumDef>of(), |
| ImmutableList.<AtlasStructDef>of(), |
| ImmutableList.<AtlasClassificationDef>of(), |
| ImmutableList.<AtlasEntityDef>of(mapValueDef, mapOwnerDef)); |
| |
| typeDefStore.createTypesDef(typesDef); |
| |
| AtlasEntityType mapOwnerType = typeRegistry.getEntityTypeByName("RequiredMapOwner"); |
| AtlasEntityType mapValueType = typeRegistry.getEntityTypeByName("RequiredMapValue"); |
| |
| // Create instances of RequiredMapOwner and RequiredMapValue. |
| // Set RequiredMapOwner.map with one entry that references RequiredMapValue instance. |
| AtlasEntity mapOwnerInstance = new AtlasEntity(mapOwnerType.getTypeName()); |
| AtlasEntity mapValueInstance = new AtlasEntity(mapValueType.getTypeName()); |
| mapOwnerInstance.setAttribute("map", Collections.singletonMap("value1", AtlasTypeUtil.getAtlasObjectId(mapValueInstance))); |
| |
| AtlasEntity.AtlasEntitiesWithExtInfo entities = new AtlasEntity.AtlasEntitiesWithExtInfo(); |
| entities.addReferredEntity(mapValueInstance); |
| entities.addEntity(mapOwnerInstance); |
| |
| List<AtlasEntityHeader> createEntitiesResult = entityStore.createOrUpdate(new AtlasEntityStream(entities), false).getCreatedEntities(); |
| Assert.assertEquals(createEntitiesResult.size(), 2); |
| List<String> guids = metadataService.getEntityList("RequiredMapOwner"); |
| Assert.assertEquals(guids.size(), 1); |
| String mapOwnerGuid = guids.get(0); |
| guids = metadataService.getEntityList("RequiredMapValue"); |
| Assert.assertEquals(guids.size(), 1); |
| String mapValueGuid = guids.get(0); |
| |
| // Verify MapOwner.map attribute has expected value. |
| final AtlasEntity.AtlasEntityWithExtInfo mapOwnerInstance1 = entityStore.getById(mapOwnerGuid); |
| Object object = mapOwnerInstance1.getEntity().getAttribute("map"); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof Map); |
| Map<String, AtlasObjectId> map = (Map<String, AtlasObjectId>)object; |
| Assert.assertEquals(map.size(), 1); |
| AtlasObjectId mapValueInstance1 = map.get("value1"); |
| Assert.assertNotNull(mapValueInstance1); |
| Assert.assertEquals(mapValueInstance1.getGuid(), mapValueGuid); |
| String edgeLabel = AtlasGraphUtilsV1.getAttributeEdgeLabel(mapOwnerType, "map"); |
| String mapEntryLabel = edgeLabel + "." + "value1"; |
| AtlasEdgeLabel atlasEdgeLabel = new AtlasEdgeLabel(mapEntryLabel); |
| AtlasVertex mapOwnerVertex = GraphHelper.getInstance().getVertexForGUID(mapOwnerGuid); |
| object = mapOwnerVertex.getProperty(atlasEdgeLabel.getQualifiedMapKey(), Object.class); |
| Assert.assertNotNull(object); |
| |
| // Verify deleting the target of required map attribute throws a AtlasBaseException. |
| try { |
| entityStore.deleteById(mapValueGuid); |
| Assert.fail(AtlasBaseException.class.getSimpleName() + " was expected but none thrown."); |
| } |
| catch (Exception e) { |
| verifyExceptionThrown(e, AtlasBaseException.class); |
| } |
| } |
| |
| @Test |
| public void testLowerBoundsIgnoredWhenDeletingCompositeEntitesOwnedByMap() throws Exception { |
| // Define MapValueReferencer type with required reference to CompositeMapValue. |
| AtlasStructDef.AtlasAttributeDef[] mapValueAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("refToMapValue", "CompositeMapValue", |
| false, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 1, 1, |
| false, false, |
| Collections.<AtlasStructDef.AtlasConstraintDef>emptyList()) |
| }; |
| |
| AtlasEntityDef mapValueDef = |
| new AtlasEntityDef("MapValueReferencer", "RequiredMapValue_description", "1.0", |
| Arrays.asList(mapValueAttributes), Collections.<String>emptySet()); |
| |
| |
| AtlasStructDef.AtlasAttributeDef[] mapContainerAttributes = new AtlasStructDef.AtlasAttributeDef[]{ |
| new AtlasStructDef.AtlasAttributeDef("requiredMap", "map<string,MapValueReferencer>", |
| false, |
| AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, 1, 1, |
| false, false, |
| new ArrayList<AtlasStructDef.AtlasConstraintDef>() {{ |
| add(new AtlasStructDef.AtlasConstraintDef(AtlasStructDef.AtlasConstraintDef.CONSTRAINT_TYPE_OWNED_REF)); |
| }}) |
| }; |
| |
| AtlasEntityDef mapContainerDef = |
| new AtlasEntityDef("MapValueReferencerContainer", "MapValueReferencerContainer_description", "1.0", |
| Arrays.asList(mapContainerAttributes), Collections.<String>emptySet()); |
| |
| |
| AtlasTypesDef typesDef = AtlasTypeUtil.getTypesDef(ImmutableList.<AtlasEnumDef>of(), |
| ImmutableList.<AtlasStructDef>of(), |
| ImmutableList.<AtlasClassificationDef>of(), |
| ImmutableList.<AtlasEntityDef>of(mapValueDef, mapContainerDef)); |
| |
| typeDefStore.createTypesDef(typesDef); |
| |
| // Create instances of CompositeMapOwner and CompositeMapValue. |
| // Set MapOwner.map with one entry that references MapValue instance. |
| AtlasEntity.AtlasEntityWithExtInfo entityDefinition = createMapOwnerAndValueEntities(); |
| String mapOwnerGuid = entityDefinition.getEntity().getGuid(); |
| |
| // Verify MapOwner.map attribute has expected value. |
| ITypedReferenceableInstance mapOwnerInstance = metadataService.getEntityDefinition(mapOwnerGuid); |
| Object object = mapOwnerInstance.get("map"); |
| Assert.assertNotNull(object); |
| Assert.assertTrue(object instanceof Map); |
| Map<String, ITypedReferenceableInstance> map = (Map<String, ITypedReferenceableInstance>)object; |
| Assert.assertEquals(map.size(), 1); |
| ITypedReferenceableInstance mapValueInstance = map.get("value1"); |
| Assert.assertNotNull(mapValueInstance); |
| String mapValueGuid = mapValueInstance.getId()._getId(); |
| |
| // Create instance of MapValueReferencerContainer |
| RequestContextV1.clear(); |
| AtlasEntity mapValueReferencer = new AtlasEntity(mapValueDef.getName()); |
| mapValueReferencer.setAttribute("refToMapValue", new AtlasObjectId(mapValueInstance.getId()._getId(), mapValueInstance.getTypeName())); |
| AtlasEntity.AtlasEntitiesWithExtInfo entities = new AtlasEntity.AtlasEntitiesWithExtInfo(); |
| entities.addEntity(mapValueReferencer); |
| |
| List<AtlasEntityHeader> createEntitiesResult = entityStore.createOrUpdate(new AtlasEntityStream(entities), false).getCreatedEntities(); |
| Assert.assertEquals(createEntitiesResult.size(), 1); |
| |
| // Create instance of MapValueReferencer, and update mapValueReferencerContainer |
| // to reference it. |
| AtlasEntity mapValueReferenceContainer = new AtlasEntity(mapContainerDef.getName()); |
| entities = new AtlasEntity.AtlasEntitiesWithExtInfo(); |
| entities.addEntity(mapValueReferenceContainer); |
| entities.addReferredEntity(mapValueReferencer); |
| mapValueReferenceContainer.setAttribute("requiredMap", Collections.singletonMap("value1", AtlasTypeUtil.getAtlasObjectId(mapValueReferencer))); |
| |
| |
| RequestContextV1.clear(); |
| EntityMutationResponse updateEntitiesResult = entityStore.createOrUpdate(new AtlasEntityStream(entities), false); |
| |
| String mapValueReferencerContainerGuid = updateEntitiesResult.getCreatedEntitiesByTypeName("MapValueReferencerContainer").get(0).getGuid(); |
| String mapValueReferencerGuid = updateEntitiesResult.getUpdatedEntitiesByTypeName("MapValueReferencer").get(0).getGuid(); |
| |
| Assert.assertEquals(updateEntitiesResult.getCreatedEntities().size(), 1); |
| Assert.assertEquals(updateEntitiesResult.getUpdatedEntities().size(), 1); |
| Assert.assertEquals(updateEntitiesResult.getUpdatedEntities().get(0).getGuid(), mapValueReferencerGuid); |
| |
| |
| // Delete map owner and map referencer container. A total of 4 entities should be deleted, |
| // including the composite entities. The lower bound constraint on MapValueReferencer.refToMapValue |
| // should not be enforced on the composite MapValueReferencer since it is being deleted. |
| EntityMutationResponse deleteEntitiesResult = entityStore.deleteByIds(Arrays.asList(mapOwnerGuid, mapValueReferencerContainerGuid)); |
| Assert.assertEquals(deleteEntitiesResult.getDeletedEntities().size(), 4); |
| Assert.assertTrue(extractGuids(deleteEntitiesResult.getDeletedEntities()).containsAll( |
| Arrays.asList(mapOwnerGuid, mapValueGuid, mapValueReferencerContainerGuid, mapValueReferencerGuid))); |
| } |
| |
| private AtlasEntity.AtlasEntityWithExtInfo createMapOwnerAndValueEntities() |
| throws AtlasException, AtlasBaseException { |
| |
| final AtlasEntity mapOwnerInstance = new AtlasEntity(compositeMapOwnerType.getTypeName()); |
| mapOwnerInstance.setAttribute(NAME, TestUtils.randomString()); |
| AtlasEntity mapValueInstance = new AtlasEntity(compositeMapValueType.getTypeName()); |
| mapValueInstance.setAttribute(NAME, TestUtils.randomString()); |
| mapOwnerInstance.setAttribute("map", Collections.singletonMap("value1", AtlasTypeUtil.getAtlasObjectId(mapValueInstance))); |
| |
| AtlasEntity.AtlasEntitiesWithExtInfo entities = new AtlasEntity.AtlasEntitiesWithExtInfo(); |
| entities.addReferredEntity(mapValueInstance); |
| entities.addEntity(mapOwnerInstance); |
| |
| List<AtlasEntityHeader> createEntitiesResult = entityStore.createOrUpdate(new AtlasEntityStream(entities), false).getCreatedEntities(); |
| Assert.assertEquals(createEntitiesResult.size(), 2); |
| AtlasEntity.AtlasEntityWithExtInfo entityDefinition = entityStore.getByUniqueAttributes(compositeMapOwnerType, |
| new HashMap<String, Object>() {{ |
| put(NAME, mapOwnerInstance.getAttribute(NAME)); |
| }}); |
| return entityDefinition; |
| } |
| |
| |
| protected abstract void assertTestDisconnectUnidirectionalArrayReferenceFromStructAndTraitTypes( |
| String structContainerGuid) throws Exception; |
| |
| protected abstract void assertVerticesDeleted(List<AtlasVertex> vertices); |
| |
| protected List<AtlasVertex> getVertices(String propertyName, Object value) { |
| AtlasGraph graph = TestUtils.getGraph(); |
| Iterable<AtlasVertex> vertices = graph.getVertices(propertyName, value); |
| List<AtlasVertex> list = new ArrayList<>(); |
| for (AtlasVertex vertex : vertices) { |
| list.add(vertex); |
| } |
| return list; |
| } |
| |
| /** |
| * Search exception cause chain for specified exception. |
| * |
| * @param thrown root of thrown exception chain |
| * @param expected class of expected exception |
| */ |
| private void verifyExceptionThrown(Exception thrown, Class expected) { |
| |
| boolean exceptionFound = false; |
| Throwable cause = thrown; |
| while (cause != null) { |
| if (expected.isInstance(cause)) { |
| // good |
| exceptionFound = true; |
| break; |
| } |
| else { |
| cause = cause.getCause(); |
| } |
| } |
| if (!exceptionFound) { |
| Assert.fail(expected.getSimpleName() + " was expected but not thrown", thrown); |
| } |
| } |
| |
| } |