http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1Test.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1Test.java b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1Test.java new file mode 100644 index 0000000..d207a69 --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1Test.java @@ -0,0 +1,623 @@ +/** + * 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 + * + * 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 org.apache.atlas.repository.store.graph.v1; + +import com.google.common.collect.ImmutableList; +import org.apache.atlas.RequestContextV1; +import org.apache.atlas.TestModules; +import org.apache.atlas.TestUtilsV2; +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.model.instance.AtlasEntity; +import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo; +import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo; +import org.apache.atlas.model.instance.AtlasEntityHeader; +import org.apache.atlas.model.instance.AtlasObjectId; +import org.apache.atlas.model.instance.AtlasRelatedObjectId; +import org.apache.atlas.model.instance.EntityMutationResponse; +import org.apache.atlas.model.typedef.AtlasTypesDef; +import org.apache.atlas.repository.graph.AtlasGraphProvider; +import org.apache.atlas.repository.graph.GraphBackedSearchIndexer; +import org.apache.atlas.repository.store.bootstrap.AtlasTypeDefStoreInitializer; +import org.apache.atlas.repository.store.graph.AtlasEntityStore; +import org.apache.atlas.repository.store.graph.AtlasRelationshipStore; +import org.apache.atlas.store.AtlasTypeDefStore; +import org.apache.atlas.type.AtlasEntityType; +import org.apache.atlas.type.AtlasTypeRegistry; +import org.apache.commons.collections.CollectionUtils; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.apache.atlas.TestRelationshipUtilsV2.EMPLOYEE_TYPE; +import static org.apache.atlas.TestRelationshipUtilsV2.getDepartmentEmployeeInstances; +import static org.apache.atlas.TestRelationshipUtilsV2.getDepartmentEmployeeTypes; +import static org.apache.atlas.TestRelationshipUtilsV2.getInverseReferenceTestTypes; +import static org.apache.atlas.TestUtilsV2.NAME; +import static org.apache.atlas.type.AtlasTypeUtil.getAtlasObjectId; +import static org.mockito.Mockito.mock; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +@Guice(modules = TestModules.TestOnlyModule.class) +public abstract class AtlasRelationshipStoreV1Test { + + @Inject + AtlasTypeRegistry typeRegistry; + + @Inject + AtlasTypeDefStore typeDefStore; + + @Inject + DeleteHandlerV1 deleteHandler; + + @Inject + EntityGraphMapper graphMapper; + + AtlasEntityStore entityStore; + AtlasRelationshipStore relationshipStore; + AtlasEntityChangeNotifier mockChangeNotifier = mock(AtlasEntityChangeNotifier.class); + + protected Map<String, AtlasObjectId> employeeNameIdMap = new HashMap<>(); + + @BeforeClass + public void setUp() throws Exception { + new GraphBackedSearchIndexer(typeRegistry); + + // create employee relationship types + AtlasTypesDef employeeTypes = getDepartmentEmployeeTypes(); + typeDefStore.createTypesDef(employeeTypes); + + AtlasEntitiesWithExtInfo employeeInstances = getDepartmentEmployeeInstances(); + EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(employeeInstances), false); + + for (AtlasEntityHeader entityHeader : response.getCreatedEntities()) { + employeeNameIdMap.put((String) entityHeader.getAttribute(NAME), getAtlasObjectId(entityHeader)); + } + + init(); + AtlasTypesDef typesDef = getInverseReferenceTestTypes(); + + AtlasTypesDef typesToCreate = AtlasTypeDefStoreInitializer.getTypesToCreate(typesDef, typeRegistry); + + if (!typesToCreate.isEmpty()) { + typeDefStore.createTypesDef(typesToCreate); + } + } + + @BeforeTest + public void init() throws Exception { + entityStore = new AtlasEntityStoreV1(deleteHandler, typeRegistry, mockChangeNotifier, graphMapper); + relationshipStore = new AtlasRelationshipStoreV1(typeRegistry); + + RequestContextV1.clear(); + RequestContextV1.get().setUser(TestUtilsV2.TEST_USER); + } + + @AfterClass + public void clear() { + AtlasGraphProvider.cleanup(); + } + + @Test + public void testDepartmentEmployeeEntitiesUsingRelationship() throws Exception { + AtlasObjectId hrId = employeeNameIdMap.get("hr"); + AtlasObjectId maxId = employeeNameIdMap.get("Max"); + AtlasObjectId johnId = employeeNameIdMap.get("John"); + AtlasObjectId juliusId = employeeNameIdMap.get("Julius"); + AtlasObjectId janeId = employeeNameIdMap.get("Jane"); + AtlasObjectId mikeId = employeeNameIdMap.get("Mike"); + + AtlasEntity hrDept = getEntityFromStore(hrId.getGuid()); + AtlasEntity max = getEntityFromStore(maxId.getGuid()); + AtlasEntity john = getEntityFromStore(johnId.getGuid()); + AtlasEntity julius = getEntityFromStore(juliusId.getGuid()); + AtlasEntity jane = getEntityFromStore(janeId.getGuid()); + AtlasEntity mike = getEntityFromStore(mikeId.getGuid()); + + // Department relationship attributes + List<AtlasObjectId> deptEmployees = toAtlasObjectIds(hrDept.getRelationshipAttribute("employees")); + assertNotNull(deptEmployees); + assertEquals(deptEmployees.size(), 5); + assertObjectIdsContains(deptEmployees, maxId); + assertObjectIdsContains(deptEmployees, johnId); + assertObjectIdsContains(deptEmployees, juliusId); + assertObjectIdsContains(deptEmployees, janeId); + assertObjectIdsContains(deptEmployees, mikeId); + + // Max employee validation + AtlasObjectId maxDepartmentId = toAtlasObjectId(max.getRelationshipAttribute("department")); + assertNotNull(maxDepartmentId); + assertObjectIdEquals(maxDepartmentId, hrId); + + AtlasObjectId maxManagerId = toAtlasObjectId(max.getRelationshipAttribute("manager")); + assertNotNull(maxManagerId); + assertObjectIdEquals(maxManagerId, janeId); + + List<AtlasObjectId> maxMentorsId = toAtlasObjectIds(max.getRelationshipAttribute("mentors")); + assertNotNull(maxMentorsId); + assertEquals(maxMentorsId.size(), 1); + assertObjectIdEquals(maxMentorsId.get(0), juliusId); + + List<AtlasObjectId> maxMenteesId = toAtlasObjectIds(max.getRelationshipAttribute("mentees")); + assertNotNull(maxMenteesId); + assertEquals(maxMenteesId.size(), 1); + assertObjectIdEquals(maxMenteesId.get(0), johnId); + + List<AtlasObjectId> maxFriendsIds = toAtlasObjectIds(max.getRelationshipAttribute("friends")); + assertNotNull(maxFriendsIds); + assertEquals(maxFriendsIds.size(), 2); + assertObjectIdsContains(maxFriendsIds, mikeId); + assertObjectIdsContains(maxFriendsIds, johnId); + + // John Employee validation + AtlasObjectId johnDepartmentId = toAtlasObjectId(john.getRelationshipAttribute("department")); + assertNotNull(johnDepartmentId); + assertObjectIdEquals(johnDepartmentId, hrId); + + AtlasObjectId johnManagerId = toAtlasObjectId(john.getRelationshipAttribute("manager")); + assertNotNull(johnManagerId); + assertObjectIdEquals(johnManagerId, janeId); + + List<AtlasObjectId> johnMentorIds = toAtlasObjectIds(john.getRelationshipAttribute("mentors")); + assertNotNull(johnMentorIds); + assertEquals(johnMentorIds.size(), 2); + assertObjectIdsContains(johnMentorIds, maxId); + assertObjectIdsContains(johnMentorIds, juliusId); + + List<AtlasObjectId> johnMenteesId = toAtlasObjectIds(john.getRelationshipAttribute("mentees")); + assertEmpty(johnMenteesId); + + List<AtlasObjectId> johnFriendsIds = toAtlasObjectIds(john.getRelationshipAttribute("friends")); + assertNotNull(johnFriendsIds); + assertEquals(johnFriendsIds.size(), 2); + assertObjectIdsContains(johnFriendsIds, mikeId); + assertObjectIdsContains(johnFriendsIds, maxId); + + // Mike Employee validation + AtlasObjectId mikeDepartmentId = toAtlasObjectId(mike.getRelationshipAttribute("department")); + assertNotNull(mikeDepartmentId); + assertObjectIdEquals(mikeDepartmentId, hrId); + + AtlasObjectId mikeManagerId = toAtlasObjectId(mike.getRelationshipAttribute("manager")); + assertNotNull(mikeManagerId); + assertObjectIdEquals(mikeManagerId, juliusId); + + List<AtlasObjectId> mikeMentorIds = toAtlasObjectIds(mike.getRelationshipAttribute("mentors")); + assertEmpty(mikeMentorIds); + + List<AtlasObjectId> mikeMenteesId = toAtlasObjectIds(mike.getRelationshipAttribute("mentees")); + assertEmpty(mikeMenteesId); + + List<AtlasObjectId> mikeFriendsIds = toAtlasObjectIds(mike.getRelationshipAttribute("friends")); + assertNotNull(mikeFriendsIds); + assertEquals(mikeFriendsIds.size(), 2); + assertObjectIdsContains(mikeFriendsIds, maxId); + assertObjectIdsContains(mikeFriendsIds, johnId); + + // Jane Manager validation + AtlasObjectId janeDepartmentId = toAtlasObjectId(jane.getRelationshipAttribute("department")); + assertNotNull(janeDepartmentId); + assertObjectIdEquals(janeDepartmentId, hrId); + + AtlasObjectId janeManagerId = toAtlasObjectId(jane.getRelationshipAttribute("manager")); + assertNull(janeManagerId); + + List<AtlasObjectId> janeMentorIds = toAtlasObjectIds(jane.getRelationshipAttribute("mentors")); + assertEmpty(janeMentorIds); + + List<AtlasObjectId> janeMenteesId = toAtlasObjectIds(jane.getRelationshipAttribute("mentees")); + assertEmpty(janeMenteesId); + + List<AtlasObjectId> janeSubordinateIds = toAtlasObjectIds(jane.getRelationshipAttribute("subordinates")); + assertNotNull(janeSubordinateIds); + assertEquals(janeSubordinateIds.size(), 2); + assertObjectIdsContains(janeSubordinateIds, maxId); + assertObjectIdsContains(janeSubordinateIds, johnId); + + List<AtlasObjectId> janeFriendsIds = toAtlasObjectIds(jane.getRelationshipAttribute("friends")); + assertEmpty(janeFriendsIds); + + AtlasObjectId janeSiblingId = toAtlasObjectId(jane.getRelationshipAttribute("sibling")); + assertNotNull(janeSiblingId); + assertObjectIdEquals(janeSiblingId, juliusId); + + // Julius Manager validation + AtlasObjectId juliusDepartmentId = toAtlasObjectId(julius.getRelationshipAttribute("department")); + assertNotNull(juliusDepartmentId); + assertObjectIdEquals(juliusDepartmentId, hrId); + + AtlasObjectId juliusManagerId = toAtlasObjectId(julius.getRelationshipAttribute("manager")); + assertNull(juliusManagerId); + + List<AtlasObjectId> juliusMentorIds = toAtlasObjectIds(julius.getRelationshipAttribute("mentors")); + assertEmpty(juliusMentorIds); + + List<AtlasObjectId> juliusMenteesId = toAtlasObjectIds(julius.getRelationshipAttribute("mentees")); + assertNotNull(juliusMenteesId); + assertEquals(juliusMenteesId.size(), 2); + assertObjectIdsContains(juliusMenteesId, maxId); + assertObjectIdsContains(juliusMenteesId, johnId); + + List<AtlasObjectId> juliusSubordinateIds = toAtlasObjectIds(julius.getRelationshipAttribute("subordinates")); + assertNotNull(juliusSubordinateIds); + assertEquals(juliusSubordinateIds.size(), 1); + assertObjectIdsContains(juliusSubordinateIds, mikeId); + + List<AtlasObjectId> juliusFriendsIds = toAtlasObjectIds(julius.getRelationshipAttribute("friends")); + assertEmpty(juliusFriendsIds); + + AtlasObjectId juliusSiblingId = toAtlasObjectId(julius.getRelationshipAttribute("sibling")); + assertNotNull(juliusSiblingId); + assertObjectIdEquals(juliusSiblingId, janeId); + } + + // Seeing intermittent failures with janus profile, disabling it until its fixed. + @Test(enabled = false) + public void testRelationshipAttributeUpdate_NonComposite_OneToMany() throws Exception { + AtlasObjectId maxId = employeeNameIdMap.get("Max"); + AtlasObjectId juliusId = employeeNameIdMap.get("Julius"); + AtlasObjectId janeId = employeeNameIdMap.get("Jane"); + AtlasObjectId mikeId = employeeNameIdMap.get("Mike"); + AtlasObjectId johnId = employeeNameIdMap.get("John"); + + // Change Max's Employee.manager reference to Julius and apply the change as a partial update. + // This should also update Julius to add Max to the inverse Manager.subordinates reference. + AtlasEntity maxEntityForUpdate = new AtlasEntity(EMPLOYEE_TYPE); + maxEntityForUpdate.setRelationshipAttribute("manager", juliusId); + + AtlasEntityType employeeType = typeRegistry.getEntityTypeByName(EMPLOYEE_TYPE); + Map<String, Object> uniqAttributes = Collections.<String, Object>singletonMap("name", "Max"); + EntityMutationResponse updateResponse = entityStore.updateByUniqueAttributes(employeeType, uniqAttributes , new AtlasEntityWithExtInfo(maxEntityForUpdate)); + + List<AtlasEntityHeader> partialUpdatedEntities = updateResponse.getPartialUpdatedEntities(); + assertEquals(partialUpdatedEntities.size(), 3); + // 3 entities should have been updated: + // * Max to change the Employee.manager reference + // * Julius to add Max to Manager.subordinates + // * Jane to remove Max from Manager.subordinates + + AtlasEntitiesWithExtInfo updatedEntities = entityStore.getByIds(ImmutableList.of(maxId.getGuid(), juliusId.getGuid(), janeId.getGuid())); + + // Max's manager updated as Julius + AtlasEntity maxEntity = updatedEntities.getEntity(maxId.getGuid()); + verifyRelationshipAttributeValue(maxEntity, "manager", juliusId.getGuid()); + + // Max added to the subordinate list of Julius, existing subordinate is Mike + AtlasEntity juliusEntity = updatedEntities.getEntity(juliusId.getGuid()); + verifyRelationshipAttributeList(juliusEntity, "subordinates", ImmutableList.of(maxId, mikeId)); + + // Max removed from the subordinate list of Julius + AtlasEntity janeEntity = updatedEntities.getEntity(janeId.getGuid()); + + // Jane's subordinates list includes John and Max for soft delete + // Jane's subordinates list includes only John for hard delete + verifyRelationshipAttributeUpdate_NonComposite_OneToMany(janeEntity); + + // Remove Mike from Max's friends list + // Max's current friends: [Mike, John] + // Max's updated friends: [Julius, John] + maxEntityForUpdate = new AtlasEntity(EMPLOYEE_TYPE); + maxEntityForUpdate.setRelationshipAttribute("friends", ImmutableList.of(johnId, juliusId)); + + init(); + updateResponse = entityStore.updateByUniqueAttributes(employeeType, uniqAttributes , new AtlasEntityWithExtInfo(maxEntityForUpdate)); + + partialUpdatedEntities = updateResponse.getPartialUpdatedEntities(); + assertEquals(partialUpdatedEntities.size(), 3); + // 3 entities should have been updated: + // * Max added Julius and removed Mike from Employee.friends + // * Mike removed Max from Employee.friends + // * Julius added Max in Employee.friends + + updatedEntities = entityStore.getByIds(ImmutableList.of(maxId.getGuid(), mikeId.getGuid(), johnId.getGuid(), juliusId.getGuid())); + + maxEntity = updatedEntities.getEntity(maxId.getGuid()); + juliusEntity = updatedEntities.getEntity(juliusId.getGuid()); + AtlasEntity mikeEntity = updatedEntities.getEntity(mikeId.getGuid()); + AtlasEntity johnEntity = updatedEntities.getEntity(johnId.getGuid()); + + verifyRelationshipAttributeUpdate_ManyToMany_Friends(maxEntity, juliusEntity, mikeEntity, johnEntity); + + // Remove Julius from Jane's sibling and add Mike as new sibling + AtlasEntity juliusEntityForUpdate = new AtlasEntity(EMPLOYEE_TYPE); + juliusEntityForUpdate.setRelationshipAttribute("sibling", mikeId); + + init(); + updateResponse = entityStore.updateByUniqueAttributes(employeeType, Collections.<String, Object>singletonMap("name", "Julius") , new AtlasEntityWithExtInfo(juliusEntityForUpdate)); + partialUpdatedEntities = updateResponse.getPartialUpdatedEntities(); + assertEquals(partialUpdatedEntities.size(), 3); + + updatedEntities = entityStore.getByIds(ImmutableList.of(juliusId.getGuid(), janeId.getGuid(), mikeId.getGuid())); + + juliusEntity = updatedEntities.getEntity(juliusId.getGuid()); + janeEntity = updatedEntities.getEntity(janeId.getGuid()); + mikeEntity = updatedEntities.getEntity(mikeId.getGuid()); + + verifyRelationshipAttributeUpdate_OneToOne_Sibling(juliusEntity, janeEntity, mikeEntity); + } + + @Test + public void testRelationshipAttributeUpdate_NonComposite_ManyToOne() throws Exception { + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, "a1_name"); + + AtlasEntity a2 = new AtlasEntity("A"); + a2.setAttribute(NAME, "a2_name"); + + AtlasEntity a3 = new AtlasEntity("A"); + a3.setAttribute(NAME, "a3_name"); + + AtlasEntity b = new AtlasEntity("B"); + b.setAttribute(NAME, "b_name"); + + AtlasEntitiesWithExtInfo entitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + entitiesWithExtInfo.addEntity(a1); + entitiesWithExtInfo.addEntity(a2); + entitiesWithExtInfo.addEntity(a3); + entitiesWithExtInfo.addEntity(b); + entityStore.createOrUpdate(new AtlasEntityStream(entitiesWithExtInfo) , false); + + AtlasEntity bPartialUpdate = new AtlasEntity("B"); + bPartialUpdate.setRelationshipAttribute("manyA", ImmutableList.of(getAtlasObjectId(a1), getAtlasObjectId(a2))); + + init(); + EntityMutationResponse response = entityStore.updateByUniqueAttributes(typeRegistry.getEntityTypeByName("B"), + Collections.singletonMap(NAME, b.getAttribute(NAME)), + new AtlasEntityWithExtInfo(bPartialUpdate)); + // Verify 3 entities were updated: + // * set b.manyA reference to a1 and a2 + // * set inverse a1.oneB reference to b + // * set inverse a2.oneB reference to b + assertEquals(response.getPartialUpdatedEntities().size(), 3); + AtlasEntitiesWithExtInfo updatedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), b.getGuid())); + + AtlasEntity a1Entity = updatedEntities.getEntity(a1.getGuid()); + verifyRelationshipAttributeValue(a1Entity, "oneB", b.getGuid()); + + AtlasEntity a2Entity = updatedEntities.getEntity(a2.getGuid()); + verifyRelationshipAttributeValue(a2Entity, "oneB", b.getGuid()); + + AtlasEntity bEntity = updatedEntities.getEntity(b.getGuid()); + verifyRelationshipAttributeList(bEntity, "manyA", ImmutableList.of(getAtlasObjectId(a1), getAtlasObjectId(a2))); + + + bPartialUpdate.setRelationshipAttribute("manyA", ImmutableList.of(getAtlasObjectId(a3))); + init(); + response = entityStore.updateByUniqueAttributes(typeRegistry.getEntityTypeByName("B"), + Collections.singletonMap(NAME, b.getAttribute(NAME)), + new AtlasEntityWithExtInfo(bPartialUpdate)); + // Verify 4 entities were updated: + // * set b.manyA reference to a3 + // * set inverse a3.oneB reference to b + // * disconnect inverse a1.oneB reference to b + // * disconnect inverse a2.oneB reference to b + assertEquals(response.getPartialUpdatedEntities().size(), 4); + init(); + + updatedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), a3.getGuid(), b.getGuid())); + a1Entity = updatedEntities.getEntity(a1.getGuid()); + a2Entity = updatedEntities.getEntity(a2.getGuid()); + bEntity = updatedEntities.getEntity(b.getGuid()); + + AtlasEntity a3Entity = updatedEntities.getEntity(a3.getGuid()); + verifyRelationshipAttributeValue(a3Entity, "oneB", b.getGuid()); + + verifyRelationshipAttributeUpdate_NonComposite_ManyToOne(a1Entity, a2Entity, a3Entity, bEntity); + } + + @Test + public void testRelationshipAttributeUpdate_NonComposite_OneToOne() throws Exception { + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, "a1_name"); + + AtlasEntity a2 = new AtlasEntity("A"); + a2.setAttribute(NAME, "a2_name"); + + AtlasEntity b = new AtlasEntity("B"); + b.setAttribute(NAME, "b_name"); + + AtlasEntitiesWithExtInfo entitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + entitiesWithExtInfo.addEntity(a1); + entitiesWithExtInfo.addEntity(a2); + entitiesWithExtInfo.addEntity(b); + + EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(entitiesWithExtInfo) , false); + + AtlasEntity partialUpdateB = new AtlasEntity("B"); + partialUpdateB.setRelationshipAttribute("a", getAtlasObjectId(a1)); + + init(); + AtlasEntityType bType = typeRegistry.getEntityTypeByName("B"); + + response = entityStore.updateByUniqueAttributes(bType, Collections.singletonMap(NAME, b.getAttribute(NAME)), new AtlasEntityWithExtInfo(partialUpdateB)); + List<AtlasEntityHeader> partialUpdatedEntitiesHeader = response.getPartialUpdatedEntities(); + // Verify 2 entities were updated: + // * set b.a reference to a1 + // * set inverse a1.b reference to b + assertEquals(partialUpdatedEntitiesHeader.size(), 2); + AtlasEntitiesWithExtInfo partialUpdatedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), b.getGuid())); + + AtlasEntity a1Entity = partialUpdatedEntities.getEntity(a1.getGuid()); + verifyRelationshipAttributeValue(a1Entity, "b", b.getGuid()); + + AtlasEntity bEntity = partialUpdatedEntities.getEntity(b.getGuid()); + verifyRelationshipAttributeValue(bEntity, "a", a1.getGuid()); + + init(); + + // Update b.a to reference a2. + partialUpdateB.setRelationshipAttribute("a", getAtlasObjectId(a2)); + response = entityStore.updateByUniqueAttributes(bType, Collections.<String, Object>singletonMap(NAME, b.getAttribute(NAME)), new AtlasEntityWithExtInfo(partialUpdateB)); + partialUpdatedEntitiesHeader = response.getPartialUpdatedEntities(); + // Verify 3 entities were updated: + // * set b.a reference to a2 + // * set a2.b reference to b + // * disconnect a1.b reference + assertEquals(partialUpdatedEntitiesHeader.size(), 3); + partialUpdatedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), b.getGuid())); + + bEntity = partialUpdatedEntities.getEntity(b.getGuid()); + verifyRelationshipAttributeValue(bEntity, "a", a2.getGuid()); + + AtlasEntity a2Entity = partialUpdatedEntities.getEntity(a2.getGuid()); + verifyRelationshipAttributeValue(a2Entity, "b", b.getGuid()); + + a1Entity = partialUpdatedEntities.getEntity(a1.getGuid()); + verifyRelationshipAttributeUpdate_NonComposite_OneToOne(a1Entity, bEntity); + } + + @Test + public void testRelationshipAttributeUpdate_NonComposite_ManyToMany() throws Exception { + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, "a1_name"); + + AtlasEntity a2 = new AtlasEntity("A"); + a2.setAttribute(NAME, "a2_name"); + + AtlasEntity a3 = new AtlasEntity("A"); + a3.setAttribute(NAME, "a3_name"); + + AtlasEntity b1 = new AtlasEntity("B"); + b1.setAttribute(NAME, "b1_name"); + + AtlasEntity b2 = new AtlasEntity("B"); + b2.setAttribute(NAME, "b2_name"); + + AtlasEntitiesWithExtInfo entitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + entitiesWithExtInfo.addEntity(a1); + entitiesWithExtInfo.addEntity(a2); + entitiesWithExtInfo.addEntity(a3); + entitiesWithExtInfo.addEntity(b1); + entitiesWithExtInfo.addEntity(b2); + entityStore.createOrUpdate(new AtlasEntityStream(entitiesWithExtInfo) , false); + + AtlasEntity b1PartialUpdate = new AtlasEntity("B"); + b1PartialUpdate.setRelationshipAttribute("manyToManyA", ImmutableList.of(getAtlasObjectId(a1), getAtlasObjectId(a2))); + + init(); + EntityMutationResponse response = entityStore.updateByUniqueAttributes(typeRegistry.getEntityTypeByName("B"), + Collections.singletonMap(NAME, b1.getAttribute(NAME)), + new AtlasEntityWithExtInfo(b1PartialUpdate)); + + List<AtlasEntityHeader> updatedEntityHeaders = response.getPartialUpdatedEntities(); + assertEquals(updatedEntityHeaders.size(), 3); + + AtlasEntitiesWithExtInfo updatedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), b1.getGuid())); + + AtlasEntity b1Entity = updatedEntities.getEntity(b1.getGuid()); + verifyRelationshipAttributeList(b1Entity, "manyToManyA", ImmutableList.of(getAtlasObjectId(a1), getAtlasObjectId(a2))); + + AtlasEntity a1Entity = updatedEntities.getEntity(a1.getGuid()); + verifyRelationshipAttributeList(a1Entity, "manyB", ImmutableList.of(getAtlasObjectId(b1))); + + AtlasEntity a2Entity = updatedEntities.getEntity(a2.getGuid()); + verifyRelationshipAttributeList(a2Entity, "manyB", ImmutableList.of(getAtlasObjectId(b1))); + } + + protected abstract void verifyRelationshipAttributeUpdate_NonComposite_OneToOne(AtlasEntity a1, AtlasEntity b); + + protected abstract void verifyRelationshipAttributeUpdate_NonComposite_OneToMany(AtlasEntity entity) throws Exception; + + protected abstract void verifyRelationshipAttributeUpdate_NonComposite_ManyToOne(AtlasEntity a1, AtlasEntity a2, AtlasEntity a3, AtlasEntity b); + + protected abstract void verifyRelationshipAttributeUpdate_ManyToMany_Friends(AtlasEntity e1, AtlasEntity e2, AtlasEntity e3, AtlasEntity e4) throws Exception; + + protected abstract void verifyRelationshipAttributeUpdate_OneToOne_Sibling(AtlasEntity e1, AtlasEntity e2, AtlasEntity e3) throws Exception; + + protected static void assertObjectIdsContains(List<AtlasObjectId> objectIds, AtlasObjectId objectId) { + assertTrue(CollectionUtils.isNotEmpty(objectIds)); + assertTrue(objectIds.contains(objectId)); + } + + protected static void assertObjectIdEquals(AtlasObjectId objId1, AtlasObjectId objId2) { + assertTrue(objId1.equals(objId2)); + } + + private static void assertEmpty(List collection) { + assertTrue(collection != null && collection.isEmpty()); + } + + protected static List<AtlasObjectId> toAtlasObjectIds(Object object) { + List<AtlasObjectId> ret = new ArrayList<>(); + + if (object instanceof List) { + List<?> objectIds = (List) object; + + if (CollectionUtils.isNotEmpty(objectIds)) { + for (Object obj : objectIds) { + if (obj instanceof AtlasRelatedObjectId) { + AtlasRelatedObjectId relatedObjectId = (AtlasRelatedObjectId) obj; + ret.add(new AtlasObjectId(relatedObjectId.getGuid(), relatedObjectId.getTypeName(), relatedObjectId.getUniqueAttributes())); + } + } + } + } + + return ret; + } + + protected static AtlasObjectId toAtlasObjectId(Object object) { + if (object instanceof AtlasRelatedObjectId) { + AtlasRelatedObjectId relatedObjectId = (AtlasRelatedObjectId) object; + return new AtlasObjectId(relatedObjectId.getGuid(), relatedObjectId.getTypeName(), relatedObjectId.getUniqueAttributes()); + } + + return null; + } + + private AtlasEntity getEntityFromStore(String guid) throws AtlasBaseException { + AtlasEntityWithExtInfo entity = guid != null ? entityStore.getById(guid) : null; + + return entity != null ? entity.getEntity() : null; + } + + protected static void verifyRelationshipAttributeList(AtlasEntity entity, String relationshipAttrName, List<AtlasObjectId> expectedValues) { + Object refValue = entity.getRelationshipAttribute(relationshipAttrName); + assertTrue(refValue instanceof List); + + List<AtlasObjectId> refList = toAtlasObjectIds(refValue); + assertEquals(refList.size(), expectedValues.size()); + + if (expectedValues.size() > 0) { + assertTrue(refList.containsAll(expectedValues)); + } + } + + protected static void verifyRelationshipAttributeValue(AtlasEntity entity, String relationshipAttrName, String expectedGuid) { + Object refValue = entity.getRelationshipAttribute(relationshipAttrName); + if (expectedGuid == null) { + assertNull(refValue); + } + else { + assertTrue(refValue instanceof AtlasObjectId); + AtlasObjectId referencedObjectId = (AtlasObjectId) refValue; + assertEquals(referencedObjectId.getGuid(), expectedGuid); + } + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateHardDeleteV1Test.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateHardDeleteV1Test.java b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateHardDeleteV1Test.java new file mode 100644 index 0000000..d54adeb --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateHardDeleteV1Test.java @@ -0,0 +1,75 @@ +/** + * 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 + * + * 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 org.apache.atlas.repository.store.graph.v1; + +import com.google.common.collect.ImmutableList; +import org.apache.atlas.TestModules; +import org.apache.atlas.model.instance.AtlasEntity; +import org.apache.atlas.model.instance.AtlasObjectId; +import org.apache.atlas.type.AtlasTypeUtil; +import org.testng.annotations.Guice; + +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * Inverse reference update test with {@link HardDeleteHandlerV1} + */ +@Guice(modules = TestModules.HardDeleteModule.class) +public class InverseReferenceUpdateHardDeleteV1Test extends InverseReferenceUpdateV1Test { + + @Override + protected void verify_testInverseReferenceAutoUpdate_NonComposite_OneToMany(AtlasEntity jane) throws Exception { + + // Max should have been removed from the subordinates list, leaving only John. + verifyReferenceList(jane, "subordinates", ImmutableList.of(nameIdMap.get("John"))); + } + + @Override + protected void verify_testInverseReferenceAutoUpdate_NonCompositeManyToOne(AtlasEntity a1, AtlasEntity a2, AtlasEntity a3, AtlasEntity b) { + + verifyReferenceValue(a1, "oneB", null); + + verifyReferenceValue(a2, "oneB", null); + + verifyReferenceList(b, "manyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a3))); + } + + @Override + protected void verify_testInverseReferenceAutoUpdate_NonComposite_OneToOne(AtlasEntity a1, AtlasEntity b) { + + verifyReferenceValue(a1, "b", null); + } + + @Override + protected void verify_testInverseReferenceAutoUpdate_Map(AtlasEntity a1, AtlasEntity b1, + AtlasEntity b2, AtlasEntity b3) { + + Object value = a1.getAttribute("mapToB"); + assertTrue(value instanceof Map); + Map<String, AtlasObjectId> refMap = (Map<String, AtlasObjectId>) value; + assertEquals(refMap.size(), 1); + AtlasObjectId referencedEntityId = refMap.get("b3"); + assertEquals(referencedEntityId, AtlasTypeUtil.getAtlasObjectId(b3)); + verifyReferenceValue(b1, "mappedFromA", null); + verifyReferenceValue(b2, "mappedFromA", null); + } + +} http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateSoftDeleteV1Test.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateSoftDeleteV1Test.java b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateSoftDeleteV1Test.java new file mode 100644 index 0000000..884ab54 --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateSoftDeleteV1Test.java @@ -0,0 +1,78 @@ +/** + * 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 + * + * 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 org.apache.atlas.repository.store.graph.v1; + +import com.google.common.collect.ImmutableList; +import org.apache.atlas.TestModules; +import org.apache.atlas.model.instance.AtlasEntity; +import org.apache.atlas.model.instance.AtlasObjectId; +import org.apache.atlas.type.AtlasTypeUtil; +import org.testng.annotations.Guice; + +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + + +/** + * Inverse reference update test with {@link SoftDeleteHandlerV1} + */ +@Guice(modules = TestModules.SoftDeleteModule.class) +public class InverseReferenceUpdateSoftDeleteV1Test extends InverseReferenceUpdateV1Test { + + @Override + protected void verify_testInverseReferenceAutoUpdate_NonComposite_OneToMany(AtlasEntity jane) + throws Exception { + + // Max is still in the subordinates list, as the edge still exists with state DELETED + verifyReferenceList(jane, "subordinates", ImmutableList.of(nameIdMap.get("John"), nameIdMap.get("Max"))); + } + + @Override + protected void verify_testInverseReferenceAutoUpdate_NonCompositeManyToOne(AtlasEntity a1, + AtlasEntity a2, AtlasEntity a3, AtlasEntity b) { + + verifyReferenceValue(a1, "oneB", b.getGuid()); + + verifyReferenceValue(a2, "oneB", b.getGuid()); + + verifyReferenceList(b, "manyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a1), AtlasTypeUtil.getAtlasObjectId(a2), AtlasTypeUtil.getAtlasObjectId(a3))); + } + + @Override + protected void verify_testInverseReferenceAutoUpdate_NonComposite_OneToOne(AtlasEntity a1, AtlasEntity b) { + + verifyReferenceValue(a1, "b", b.getGuid()); + } + + @Override + protected void verify_testInverseReferenceAutoUpdate_Map(AtlasEntity a1, AtlasEntity b1, + AtlasEntity b2, AtlasEntity b3) { + + Object value = a1.getAttribute("mapToB"); + assertTrue(value instanceof Map); + Map<String, AtlasObjectId> refMap = (Map<String, AtlasObjectId>) value; + assertEquals(refMap.size(), 3); + AtlasObjectId referencedEntityId = refMap.get("b3"); + assertEquals(referencedEntityId, AtlasTypeUtil.getAtlasObjectId(b3)); + verifyReferenceValue(b1, "mappedFromA", a1.getGuid()); + verifyReferenceValue(b2, "mappedFromA", a1.getGuid()); + } + +} http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateV1Test.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateV1Test.java b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateV1Test.java new file mode 100644 index 0000000..2c21638 --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/repository/store/graph/v1/InverseReferenceUpdateV1Test.java @@ -0,0 +1,374 @@ +/** + * 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 + * + * 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 org.apache.atlas.repository.store.graph.v1; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.atlas.RequestContextV1; +import org.apache.atlas.TestUtilsV2; +import org.apache.atlas.model.instance.AtlasEntity; +import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo; +import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo; +import org.apache.atlas.model.instance.AtlasEntityHeader; +import org.apache.atlas.model.instance.AtlasObjectId; +import org.apache.atlas.model.instance.EntityMutationResponse; +import org.apache.atlas.model.typedef.AtlasTypesDef; +import org.apache.atlas.repository.graph.AtlasGraphProvider; +import org.apache.atlas.repository.store.bootstrap.AtlasTypeDefStoreInitializer; +import org.apache.atlas.repository.store.graph.AtlasEntityStore; +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.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import static org.apache.atlas.TestUtilsV2.NAME; + +/** + * Test automatic inverse reference updating in V1 (V2?) code path. + * + */ +public abstract class InverseReferenceUpdateV1Test { + @Inject + AtlasTypeRegistry typeRegistry; + + @Inject + AtlasTypeDefStore typeDefStore; + + @Inject + AtlasEntityStore entityStore; + + private AtlasEntitiesWithExtInfo deptEntity; + + protected Map<String, AtlasObjectId> nameIdMap = new HashMap<>(); + + @BeforeClass + public void setUp() throws Exception { + RequestContextV1.clear(); + RequestContextV1.get().setUser(TestUtilsV2.TEST_USER); + + AtlasTypesDef[] testTypesDefs = new AtlasTypesDef[] { TestUtilsV2.defineDeptEmployeeTypes(), + TestUtilsV2.defineInverseReferenceTestTypes() + }; + + for (AtlasTypesDef typesDef : testTypesDefs) { + AtlasTypesDef typesToCreate = AtlasTypeDefStoreInitializer.getTypesToCreate(typesDef, typeRegistry); + + if (!typesToCreate.isEmpty()) { + typeDefStore.createTypesDef(typesToCreate); + } + } + + deptEntity = TestUtilsV2.createDeptEg2(); + init(); + EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(deptEntity), false); + for (AtlasEntityHeader entityHeader : response.getCreatedEntities()) { + nameIdMap.put((String)entityHeader.getAttribute(NAME), AtlasTypeUtil.getAtlasObjectId(entityHeader)); + } + } + + @AfterClass + public void clear() { + AtlasGraphProvider.cleanup(); + } + + @BeforeMethod + public void init() throws Exception { + RequestContextV1.clear(); + RequestContextV1.get().setUser(TestUtilsV2.TEST_USER); + } + + @Test + public void testInverseReferenceAutoUpdate_NonComposite_OneToMany() throws Exception { + AtlasObjectId juliusId = nameIdMap.get("Julius"); + + // Change Max's Employee.manager reference to Julius and apply the change as a partial update. + // This should also update Julius to add Max to the inverse Manager.subordinates reference. + AtlasEntity maxEntityForUpdate = new AtlasEntity(TestUtilsV2.EMPLOYEE_TYPE); + maxEntityForUpdate.setAttribute("manager", juliusId); + AtlasEntityType employeeType = typeRegistry.getEntityTypeByName(TestUtilsV2.EMPLOYEE_TYPE); + Map<String, Object> uniqAttributes = Collections.<String, Object>singletonMap("name", "Max"); + EntityMutationResponse updateResponse = entityStore.updateByUniqueAttributes(employeeType, uniqAttributes , new AtlasEntityWithExtInfo(maxEntityForUpdate)); + List<AtlasEntityHeader> partialUpdatedEntities = updateResponse.getPartialUpdatedEntities(); + // 3 entities should have been updated: + // * Max to change the Employee.manager reference + // * Julius to add Max to Manager.subordinates + // * Jane to remove Max from Manager.subordinates + assertEquals(partialUpdatedEntities.size(), 3); + + AtlasObjectId maxId = nameIdMap.get("Max"); + String janeGuid = nameIdMap.get("Jane").getGuid(); + AtlasEntitiesWithExtInfo storedEntities = entityStore.getByIds(ImmutableList.of(maxId.getGuid(), juliusId.getGuid(), janeGuid)); + AtlasEntity storedEntity = storedEntities.getEntity(maxId.getGuid()); + verifyReferenceValue(storedEntity, "manager", juliusId.getGuid()); + storedEntity = storedEntities.getEntity(juliusId.getGuid()); + verifyReferenceList(storedEntity, "subordinates", ImmutableList.of(maxId)); + storedEntity = storedEntities.getEntity(janeGuid); + verify_testInverseReferenceAutoUpdate_NonComposite_OneToMany(storedEntity); + } + + protected abstract void verify_testInverseReferenceAutoUpdate_NonComposite_OneToMany(AtlasEntity jane) throws Exception; + + @Test + public void testInverseReferenceAutoUpdate_NonCompositeManyToOne() throws Exception { + AtlasEntityType bType = typeRegistry.getEntityTypeByName("B"); + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity a2 = new AtlasEntity("A"); + a2.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity a3 = new AtlasEntity("A"); + a3.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b = new AtlasEntity("B"); + + b.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntitiesWithExtInfo atlasEntitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + atlasEntitiesWithExtInfo.addEntity(a1); + atlasEntitiesWithExtInfo.addEntity(a2); + atlasEntitiesWithExtInfo.addEntity(a3); + atlasEntitiesWithExtInfo.addEntity(b); + AtlasEntityStream entityStream = new AtlasEntityStream(atlasEntitiesWithExtInfo); + EntityMutationResponse response = entityStore.createOrUpdate(entityStream , false); + + AtlasEntity bForPartialUpdate = new AtlasEntity("B"); + bForPartialUpdate.setAttribute("manyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a1), AtlasTypeUtil.getAtlasObjectId(a2))); + init(); + response = entityStore.updateByUniqueAttributes(bType, Collections.<String, Object>singletonMap(NAME, b.getAttribute(NAME)), new AtlasEntityWithExtInfo(bForPartialUpdate)); + List<AtlasEntityHeader> partialUpdatedEntities = response.getPartialUpdatedEntities(); + // Verify 3 entities were updated: + // * set b.manyA reference to a1 and a2 + // * set inverse a1.oneB reference to b + // * set inverse a2.oneB reference to b + assertEquals(partialUpdatedEntities.size(), 3); + AtlasEntitiesWithExtInfo storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), b.getGuid())); + AtlasEntity storedEntity = storedEntities.getEntity(a1.getGuid()); + verifyReferenceValue(storedEntity, "oneB", b.getGuid()); + + storedEntity = storedEntities.getEntity(a2.getGuid()); + verifyReferenceValue(storedEntity, "oneB", b.getGuid()); + + storedEntity = storedEntities.getEntity(b.getGuid()); + verifyReferenceList(storedEntity, "manyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a1), AtlasTypeUtil.getAtlasObjectId(a2))); + + bForPartialUpdate.setAttribute("manyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a3))); + init(); + response = entityStore.updateByUniqueAttributes(bType, Collections.<String, Object>singletonMap(NAME, b.getAttribute(NAME)), new AtlasEntityWithExtInfo(bForPartialUpdate)); + partialUpdatedEntities = response.getPartialUpdatedEntities(); + // Verify 4 entities were updated: + // * set b.manyA reference to a3 + // * set inverse a3.oneB reference to b + // * disconnect inverse a1.oneB reference to b + // * disconnect inverse a2.oneB reference to b + assertEquals(partialUpdatedEntities.size(), 4); + + init(); + storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), a3.getGuid(), b.getGuid())); + verifyReferenceValue(storedEntities.getEntity(a3.getGuid()), "oneB", b.getGuid()); + + verify_testInverseReferenceAutoUpdate_NonCompositeManyToOne(storedEntities.getEntity(a1.getGuid()), storedEntities.getEntity(a2.getGuid()), + storedEntities.getEntity(a3.getGuid()), storedEntities.getEntity(b.getGuid())); + } + + protected abstract void verify_testInverseReferenceAutoUpdate_NonCompositeManyToOne(AtlasEntity a1, AtlasEntity a2, AtlasEntity a3, AtlasEntity b); + + @Test + public void testInverseReferenceAutoUpdate_NonComposite_OneToOne() throws Exception { + AtlasEntityType bType = typeRegistry.getEntityTypeByName("B"); + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity a2 = new AtlasEntity("A"); + a2.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b = new AtlasEntity("B"); + b.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntitiesWithExtInfo atlasEntitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + atlasEntitiesWithExtInfo.addEntity(a1); + atlasEntitiesWithExtInfo.addEntity(a2); + atlasEntitiesWithExtInfo.addEntity(b); + AtlasEntityStream entityStream = new AtlasEntityStream(atlasEntitiesWithExtInfo); + EntityMutationResponse response = entityStore.createOrUpdate(entityStream , false); + + AtlasEntity bForPartialUpdate = new AtlasEntity("B"); + bForPartialUpdate.setAttribute("a", AtlasTypeUtil.getAtlasObjectId(a1)); + init(); + response = entityStore.updateByUniqueAttributes(bType, Collections.<String, Object>singletonMap(NAME, b.getAttribute(NAME)), new AtlasEntityWithExtInfo(bForPartialUpdate)); + List<AtlasEntityHeader> partialUpdatedEntities = response.getPartialUpdatedEntities(); + // Verify 2 entities were updated: + // * set b.a reference to a1 + // * set inverse a1.b reference to b + assertEquals(partialUpdatedEntities.size(), 2); + AtlasEntitiesWithExtInfo storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), b.getGuid())); + AtlasEntity storedEntity = storedEntities.getEntity(a1.getGuid()); + verifyReferenceValue(storedEntity, "b", b.getGuid()); + storedEntity = storedEntities.getEntity(b.getGuid()); + verifyReferenceValue(storedEntity, "a", a1.getGuid()); + + // Update b.a to reference a2. + bForPartialUpdate.setAttribute("a", AtlasTypeUtil.getAtlasObjectId(a2)); + init(); + response = entityStore.updateByUniqueAttributes(bType, Collections.<String, Object>singletonMap(NAME, b.getAttribute(NAME)), new AtlasEntityWithExtInfo(bForPartialUpdate)); + partialUpdatedEntities = response.getPartialUpdatedEntities(); + // Verify 3 entities were updated: + // * set b.a reference to a2 + // * set a2.b reference to b + // * disconnect a1.b reference + assertEquals(partialUpdatedEntities.size(), 3); + storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), b.getGuid())); + storedEntity = storedEntities.getEntity(a2.getGuid()); + verifyReferenceValue(storedEntity, "b", b.getGuid()); + storedEntity = storedEntities.getEntity(b.getGuid()); + verifyReferenceValue(storedEntity, "a", a2.getGuid()); + storedEntity = storedEntities.getEntity(a1.getGuid()); + Object refValue = storedEntity.getAttribute("b"); + verify_testInverseReferenceAutoUpdate_NonComposite_OneToOne(storedEntities.getEntity(a1.getGuid()), storedEntities.getEntity(b.getGuid())); + } + + protected abstract void verify_testInverseReferenceAutoUpdate_NonComposite_OneToOne(AtlasEntity a1, AtlasEntity b); + + @Test + public void testInverseReferenceAutoUpdate_NonComposite_ManyToMany() throws Exception { + AtlasEntityType bType = typeRegistry.getEntityTypeByName("B"); + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity a2 = new AtlasEntity("A"); + a2.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity a3 = new AtlasEntity("A"); + a3.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b1 = new AtlasEntity("B"); + b1.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b2 = new AtlasEntity("B"); + b2.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntitiesWithExtInfo atlasEntitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + atlasEntitiesWithExtInfo.addEntity(a1); + atlasEntitiesWithExtInfo.addEntity(a2); + atlasEntitiesWithExtInfo.addEntity(a3); + atlasEntitiesWithExtInfo.addEntity(b1); + atlasEntitiesWithExtInfo.addEntity(b2); + AtlasEntityStream entityStream = new AtlasEntityStream(atlasEntitiesWithExtInfo); + EntityMutationResponse response = entityStore.createOrUpdate(entityStream , false); + + AtlasEntity b1ForPartialUpdate = new AtlasEntity("B"); + b1ForPartialUpdate.setAttribute("manyToManyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a1), AtlasTypeUtil.getAtlasObjectId(a2))); + init(); + response = entityStore.updateByUniqueAttributes(bType, Collections.<String, Object>singletonMap(NAME, b1.getAttribute(NAME)), new AtlasEntityWithExtInfo(b1ForPartialUpdate)); + List<AtlasEntityHeader> partialUpdatedEntities = response.getPartialUpdatedEntities(); + assertEquals(partialUpdatedEntities.size(), 3); + AtlasEntitiesWithExtInfo storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), a2.getGuid(), b1.getGuid())); + AtlasEntity storedEntity = storedEntities.getEntity(b1.getGuid()); + verifyReferenceList(storedEntity, "manyToManyA", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(a1), AtlasTypeUtil.getAtlasObjectId(a2))); + storedEntity = storedEntities.getEntity(a1.getGuid()); + verifyReferenceList(storedEntity, "manyB", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(b1))); + storedEntity = storedEntities.getEntity(a2.getGuid()); + verifyReferenceList(storedEntity, "manyB", ImmutableList.of(AtlasTypeUtil.getAtlasObjectId(b1))); + } + + @Test + public void testInverseReferenceAutoUpdate_Map() throws Exception { + AtlasEntity a1 = new AtlasEntity("A"); + a1.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b1 = new AtlasEntity("B"); + b1.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b2 = new AtlasEntity("B"); + b2.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntity b3 = new AtlasEntity("B"); + b3.setAttribute(NAME, TestUtilsV2.randomString()); + AtlasEntitiesWithExtInfo atlasEntitiesWithExtInfo = new AtlasEntitiesWithExtInfo(); + atlasEntitiesWithExtInfo.addEntity(a1); + atlasEntitiesWithExtInfo.addEntity(b1); + atlasEntitiesWithExtInfo.addEntity(b2); + atlasEntitiesWithExtInfo.addEntity(b3); + AtlasEntityStream entityStream = new AtlasEntityStream(atlasEntitiesWithExtInfo); + EntityMutationResponse response = entityStore.createOrUpdate(entityStream , false); + + AtlasEntityType aType = typeRegistry.getEntityTypeByName("A"); + AtlasEntity aForPartialUpdate = new AtlasEntity("A"); + aForPartialUpdate.setAttribute("mapToB", ImmutableMap.<String, AtlasObjectId>of("b1", AtlasTypeUtil.getAtlasObjectId(b1), "b2", AtlasTypeUtil.getAtlasObjectId(b2))); + init(); + response = entityStore.updateByUniqueAttributes(aType, Collections.<String, Object>singletonMap(NAME, a1.getAttribute(NAME)), new AtlasEntityWithExtInfo(aForPartialUpdate)); + List<AtlasEntityHeader> partialUpdatedEntities = response.getPartialUpdatedEntities(); + // Verify 3 entities were updated: + // * set a1.mapToB to "b1"->b1, "b2"->b2 + // * set b1.mappedFromA to a1 + // * set b2.mappedFromA to a1 + assertEquals(partialUpdatedEntities.size(), 3); + AtlasEntitiesWithExtInfo storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), b2.getGuid(), b1.getGuid())); + AtlasEntity storedEntity = storedEntities.getEntity(a1.getGuid()); + Object value = storedEntity.getAttribute("mapToB"); + assertTrue(value instanceof Map); + Map<String, AtlasObjectId> refMap = (Map<String, AtlasObjectId>) value; + assertEquals(refMap.size(), 2); + AtlasObjectId referencedEntityId = refMap.get("b1"); + assertEquals(referencedEntityId, AtlasTypeUtil.getAtlasObjectId(b1)); + referencedEntityId = refMap.get("b2"); + assertEquals(referencedEntityId, AtlasTypeUtil.getAtlasObjectId(b2)); + storedEntity = storedEntities.getEntity(b1.getGuid()); + verifyReferenceValue(storedEntity, "mappedFromA", a1.getGuid()); + storedEntity = storedEntities.getEntity(b2.getGuid()); + verifyReferenceValue(storedEntity, "mappedFromA", a1.getGuid()); + + aForPartialUpdate.setAttribute("mapToB", ImmutableMap.<String, AtlasObjectId>of("b3", AtlasTypeUtil.getAtlasObjectId(b3))); + init(); + response = entityStore.updateByUniqueAttributes(aType, Collections.<String, Object>singletonMap(NAME, a1.getAttribute(NAME)), new AtlasEntityWithExtInfo(aForPartialUpdate)); + partialUpdatedEntities = response.getPartialUpdatedEntities(); + // Verify 4 entities were updated: + // * set a1.mapToB to "b3"->b3 + // * set b3.mappedFromA to a1 + // * disconnect b1.mappedFromA + // * disconnect b2.mappedFromA + assertEquals(partialUpdatedEntities.size(), 4); + storedEntities = entityStore.getByIds(ImmutableList.of(a1.getGuid(), b2.getGuid(), b1.getGuid(), b3.getGuid())); + AtlasEntity storedB3 = storedEntities.getEntity(b3.getGuid()); + verifyReferenceValue(storedB3, "mappedFromA", a1.getGuid()); + verify_testInverseReferenceAutoUpdate_Map(storedEntities.getEntity(a1.getGuid()), storedEntities.getEntity(b1.getGuid()), storedEntities.getEntity(b2.getGuid()), storedB3); + } + + protected abstract void verify_testInverseReferenceAutoUpdate_Map(AtlasEntity a1, AtlasEntity b1, AtlasEntity b2, AtlasEntity b3); + + protected void verifyReferenceValue(AtlasEntity entity, String refName, String expectedGuid) { + Object refValue = entity.getAttribute(refName); + if (expectedGuid == null) { + assertNull(refValue); + } + else { + assertTrue(refValue instanceof AtlasObjectId); + AtlasObjectId referencedObjectId = (AtlasObjectId) refValue; + assertEquals(referencedObjectId.getGuid(), expectedGuid); + } + } + + protected void verifyReferenceList(AtlasEntity entity, String refName, List<AtlasObjectId> expectedValues) { + Object refValue = entity.getAttribute(refName); + assertTrue(refValue instanceof List); + List<AtlasObjectId> refList = (List<AtlasObjectId>) refValue; + assertEquals(refList.size(), expectedValues.size()); + if (expectedValues.size() > 0) { + assertTrue(refList.containsAll(expectedValues)); + } + } +} http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java b/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java new file mode 100644 index 0000000..0532f16 --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java @@ -0,0 +1,280 @@ +/** + * 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.userprofile; + +import org.apache.atlas.AtlasErrorCode; +import org.apache.atlas.TestModules; +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.model.SearchFilter; +import org.apache.atlas.model.discovery.SearchParameters; +import org.apache.atlas.model.profile.AtlasUserProfile; +import org.apache.atlas.model.profile.AtlasUserSavedSearch; +import org.apache.atlas.model.typedef.AtlasTypesDef; +import org.apache.atlas.repository.util.FilterUtil; +import org.apache.atlas.store.AtlasTypeDefStore; +import org.apache.atlas.type.AtlasType; +import org.apache.atlas.type.AtlasTypeRegistry; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.apache.atlas.model.profile.AtlasUserSavedSearch.SavedSearchType.BASIC; +import static org.apache.atlas.repository.impexp.ZipFileResourceTestUtils.loadModelFromJson; +import static org.testng.Assert.*; + +@Guice(modules = TestModules.TestOnlyModule.class) +public class UserProfileServiceTest { + private UserProfileService userProfileService; + private AtlasTypeDefStore typeDefStore; + private int max_searches = 4; + + @Inject + public void UserProfileServiceTest(AtlasTypeRegistry typeRegistry, + AtlasTypeDefStore typeDefStore, + UserProfileService userProfileService) throws IOException, AtlasBaseException { + this.typeDefStore = typeDefStore; + this.userProfileService = userProfileService; + + loadModelFromJson("0010-base_model.json", typeDefStore, typeRegistry); + } + + @Test + public void filterInternalType() throws AtlasBaseException { + SearchFilter searchFilter = new SearchFilter(); + AtlasTypesDef filteredTypeDefs = typeDefStore.searchTypesDef(searchFilter); + int maxTypeDefs = filteredTypeDefs.getEntityDefs().size(); + + FilterUtil.addParamsToHideInternalType(searchFilter); + filteredTypeDefs = typeDefStore.searchTypesDef(searchFilter); + + assertNotNull(filteredTypeDefs); + assertEquals(filteredTypeDefs.getEntityDefs().size(), maxTypeDefs - 3); + } + + @Test + public void createsNewProfile() throws AtlasBaseException { + int i = 0; + assertSaveLoadUserProfile(i++); + assertSaveLoadUserProfile(i); + } + + @Test(dependsOnMethods = { "createsNewProfile", "savesQueryForAnNonExistentUser" }, expectedExceptions = AtlasBaseException.class) + public void atteptsToAddAlreadyExistingQueryForAnExistingUser() throws AtlasBaseException { + SearchParameters expectedSearchParameter = getActualSearchParameters(); + + for (int i = 0; i < 2; i++) { + String userName = getIndexBasedUserName(i); + + for (int j = 0; j < max_searches; j++) { + String queryName = getIndexBasedQueryName(j); + AtlasUserSavedSearch expected = getDefaultSavedSearch(userName, queryName, expectedSearchParameter); + AtlasUserSavedSearch actual = userProfileService.addSavedSearch(expected); + + assertNotNull(actual); + assertNotNull(actual.getGuid()); + assertEquals(actual.getOwnerName(), expected.getOwnerName()); + assertEquals(actual.getName(), expected.getName()); + assertEquals(actual.getSearchType(), expected.getSearchType()); + assertEquals(actual.getSearchParameters(), expected.getSearchParameters()); + } + } + } + + @Test(dependsOnMethods = { "createsNewProfile", "savesQueryForAnNonExistentUser", "atteptsToAddAlreadyExistingQueryForAnExistingUser" }) + public void savesExistingQueryForAnExistingUser() throws AtlasBaseException { + SearchParameters expectedSearchParameter = getActualSearchParameters(); + + for (int i = 0; i < 2; i++) { + String userName = getIndexBasedUserName(i); + + for (int j = 4; j < max_searches + 6; j++) { + String queryName = getIndexBasedQueryName(j); + AtlasUserSavedSearch actual = userProfileService.addSavedSearch(getDefaultSavedSearch(userName, queryName, expectedSearchParameter)); + assertNotNull(actual); + + AtlasUserSavedSearch savedSearch = userProfileService.getSavedSearch(userName, queryName); + assertNotNull(savedSearch); + assertEquals(savedSearch.getSearchParameters(), expectedSearchParameter); + } + } + } + + private SearchParameters getActualSearchParameters() { + SearchParameters sp = new SearchParameters(); + sp.setClassification("test-classification"); + sp.setQuery("g.v().has('__guid').__guid.toList()"); + sp.setLimit(10); + sp.setTypeName("some-type"); + + return sp; + } + + @Test(dependsOnMethods = "createsNewProfile") + public void savesQueryForAnNonExistentUser() throws AtlasBaseException { + String expectedUserName = getIndexBasedUserName(0); + String expectedQueryName = "testQuery"; + SearchParameters expectedSearchParam = getActualSearchParameters(); + AtlasUserSavedSearch expectedSavedSearch = getDefaultSavedSearch(expectedUserName, expectedQueryName, expectedSearchParam); + + AtlasUserSavedSearch actual = userProfileService.addSavedSearch(expectedSavedSearch); + assertEquals(actual.getOwnerName(), expectedUserName); + assertEquals(actual.getName(), expectedQueryName); + } + + private AtlasUserSavedSearch getDefaultSavedSearch(String userName, String queryName, SearchParameters expectedSearchParam) { + return new AtlasUserSavedSearch(userName, queryName, + BASIC, expectedSearchParam); + } + + @Test(dependsOnMethods = "createsNewProfile") + public void savesMultipleQueriesForUser() throws AtlasBaseException { + final String userName = getIndexBasedUserName(0); + createUserWithSavedQueries(userName); + } + + private void createUserWithSavedQueries(String userName) throws AtlasBaseException { + SearchParameters actualSearchParameter = getActualSearchParameters(); + + saveQueries(userName, actualSearchParameter); + for (int i = 0; i < max_searches; i++) { + AtlasUserSavedSearch savedSearch = userProfileService.getSavedSearch(userName, getIndexBasedQueryName(i)); + assertEquals(savedSearch.getName(), getIndexBasedQueryName(i)); + assertEquals(savedSearch.getSearchParameters(), actualSearchParameter); + } + } + + private void saveQueries(String userName, SearchParameters sp) throws AtlasBaseException { + for (int i = 0; i < max_searches; i++) { + userProfileService.addSavedSearch(getDefaultSavedSearch(userName, getIndexBasedQueryName(i), sp)); + } + } + + @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser"}) + public void verifyQueryNameListForUser() throws AtlasBaseException { + final String userName = getIndexBasedUserName(0); + + List<AtlasUserSavedSearch> list = userProfileService.getSavedSearches(userName); + List<String> names = getIndexBasedQueryNamesList(); + for (int i = 0; i < names.size(); i++) { + assertTrue(names.contains(list.get(i).getName()), list.get(i).getName() + " failed!"); + } + } + + @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser"}, enabled = false) + public void verifyQueryConversionFromJSON() throws AtlasBaseException { + List<AtlasUserSavedSearch> list = userProfileService.getSavedSearches("first-0"); + + for (int i = 0; i < max_searches; i++) { + SearchParameters sp = list.get(i).getSearchParameters(); + String json = AtlasType.toJson(sp); + assertEquals(AtlasType.toJson(getActualSearchParameters()).replace("\n", "").replace(" ", ""), json); + } + } + + @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser"}) + public void updateSearch() throws AtlasBaseException { + final String queryName = getIndexBasedQueryName(0); + String userName = getIndexBasedUserName(0); + AtlasUserSavedSearch expected = userProfileService.getSavedSearch(userName, queryName); + assertNotNull(expected); + + SearchParameters sp = expected.getSearchParameters(); + sp.setClassification("new-classification"); + + AtlasUserSavedSearch actual = userProfileService.updateSavedSearch(expected); + + assertNotNull(actual); + assertNotNull(actual.getSearchParameters()); + assertEquals(actual.getSearchParameters().getClassification(), expected.getSearchParameters().getClassification()); + } + + @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser", "verifyQueryNameListForUser"}, expectedExceptions = AtlasBaseException.class) + public void deleteUsingGuid() throws AtlasBaseException { + final String queryName = getIndexBasedQueryName(1); + String userName = getIndexBasedUserName(0); + + AtlasUserSavedSearch expected = userProfileService.getSavedSearch(userName, queryName); + assertNotNull(expected); + + userProfileService.deleteSavedSearch(expected.getGuid()); + userProfileService.getSavedSearch(userName, queryName); + } + + @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser", "verifyQueryNameListForUser"}) + public void deleteSavedQuery() throws AtlasBaseException { + final String userName = getIndexBasedUserName(0); + AtlasUserProfile expected = userProfileService.getUserProfile(userName); + assertNotNull(expected); + + int new_max_searches = expected.getSavedSearches().size(); + String queryNameToBeDeleted = getIndexBasedQueryName(max_searches - 2); + userProfileService.deleteSearchBySearchName(userName, queryNameToBeDeleted); + + List<AtlasUserSavedSearch> savedSearchList = userProfileService.getSavedSearches(userName); + assertEquals(savedSearchList.size(), new_max_searches - 1); + } + + @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser", "verifyQueryNameListForUser"}) + void deleteUser() throws AtlasBaseException { + String userName = getIndexBasedUserName(1); + + userProfileService.deleteUserProfile(userName); + try { + userProfileService.getUserProfile(userName); + } + catch(AtlasBaseException ex) { + assertEquals(ex.getAtlasErrorCode().name(), AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND.name()); + } + } + + private void assertSaveLoadUserProfile(int i) throws AtlasBaseException { + String s = String.valueOf(i); + AtlasUserProfile expected = getAtlasUserProfile(i); + + AtlasUserProfile actual = userProfileService.saveUserProfile(expected); + assertNotNull(actual); + assertEquals(expected.getName(), actual.getName()); + assertEquals(expected.getFullName(), actual.getFullName()); + assertNotNull(actual.getGuid()); + } + + public static AtlasUserProfile getAtlasUserProfile(Integer s) { + return new AtlasUserProfile(getIndexBasedUserName(s), String.format("first-%s last-%s", s, s)); + } + + private static String getIndexBasedUserName(Integer i) { + return String.format("first-%s", i.toString()); + } + + private static String getIndexBasedQueryName(Integer i) { + return String.format("testQuery-%s", i.toString()); + } + + public List<String> getIndexBasedQueryNamesList() { + List<String> list = new ArrayList<>(); + for (int i = 0; i < max_searches; i++) { + list.add(getIndexBasedQueryName(i)); + } + + return list; + } +} http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/services/MetricsServiceTest.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/services/MetricsServiceTest.java b/repository/src/test/java/org/apache/atlas/services/MetricsServiceTest.java new file mode 100644 index 0000000..5165bcb --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/services/MetricsServiceTest.java @@ -0,0 +1,123 @@ +/** + * 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 + * + * 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 org.apache.atlas.services; + +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.model.metrics.AtlasMetrics; +import org.apache.atlas.repository.graphdb.AtlasGraph; +import org.apache.atlas.type.AtlasTypeRegistry; +import org.apache.commons.configuration.Configuration; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +public class MetricsServiceTest { + private Configuration mockConfig = mock(Configuration.class); + private AtlasTypeRegistry mockTypeRegistry = mock(AtlasTypeRegistry.class); + private AtlasGraph mockGraph = mock(AtlasGraph.class); + private MetricsService metricsService; + + private List<Map> mockMapList = new ArrayList<>(); + private Number mockCount = 10; + + @BeforeClass + public void init() throws AtlasBaseException { + Map<String, Object> mockMap = new HashMap<>(); + mockMap.put("a", 1); + mockMap.put("b", 2); + mockMap.put("c", 3); + mockMapList.add(mockMap); + + when(mockConfig.getInt(anyString(), anyInt())).thenReturn(5); + assertEquals(mockConfig.getInt("test", 1), 5); + when(mockConfig.getString(anyString(), anyString())) + .thenReturn("count()", "count()", "count()", "count()", "count()", "toList()", "count()", "toList()"); + when(mockTypeRegistry.getAllEntityDefNames()).thenReturn(Arrays.asList("a", "b", "c")); + setupMockGraph(); + + metricsService = new MetricsService(mockConfig, mockGraph); + } + + private void setupMockGraph() throws AtlasBaseException { + if (mockGraph == null) mockGraph = mock(AtlasGraph.class); + when(mockGraph.executeGremlinScript(anyString(), eq(false))).thenAnswer(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + if (((String)invocationOnMock.getArguments()[0]).contains("count()")) { + return mockCount; + } else { + return mockMapList; + } + } + }); + } + + @Test + public void testGetMetrics() throws InterruptedException, AtlasBaseException { + assertNotNull(metricsService); + AtlasMetrics metrics = metricsService.getMetrics(false); + assertNotNull(metrics); + Number aCount = metrics.getMetric("entity", "a"); + assertNotNull(aCount); + assertEquals(aCount, 1); + + Number bCount = metrics.getMetric("entity", "b"); + assertNotNull(bCount); + assertEquals(bCount, 2); + + Number cCount = metrics.getMetric("entity", "c"); + assertNotNull(cCount); + assertEquals(cCount, 3); + + Number aTags = metrics.getMetric("tag", "a"); + assertNotNull(aTags); + assertEquals(aTags, 1); + + Number bTags = metrics.getMetric("tag", "b"); + assertNotNull(bTags); + assertEquals(bTags, 2); + + Number cTags = metrics.getMetric("tag", "c"); + assertNotNull(cTags); + assertEquals(cTags, 3); + + verify(mockGraph, atLeastOnce()).executeGremlinScript(anyString(), anyBoolean()); + + // Subsequent call within the cache timeout window + metricsService.getMetrics(false); + verifyZeroInteractions(mockGraph); + + // Now test the cache refresh + Thread.sleep(6000); + metricsService.getMetrics(true); + verify(mockGraph, atLeastOnce()).executeGremlinScript(anyString(), anyBoolean()); + } +} http://git-wip-us.apache.org/repos/asf/atlas/blob/042fc557/repository/src/test/java/org/apache/atlas/utils/ObjectUpdateSynchronizerTest.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/utils/ObjectUpdateSynchronizerTest.java b/repository/src/test/java/org/apache/atlas/utils/ObjectUpdateSynchronizerTest.java new file mode 100644 index 0000000..03ebae4 --- /dev/null +++ b/repository/src/test/java/org/apache/atlas/utils/ObjectUpdateSynchronizerTest.java @@ -0,0 +1,218 @@ +/** + * 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 + * + * 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 org.apache.atlas.utils; + +import org.apache.atlas.GraphTransactionInterceptor; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.RandomStringUtils; +import org.springframework.util.CollectionUtils; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +public class ObjectUpdateSynchronizerTest { + private static final GraphTransactionInterceptor.ObjectUpdateSynchronizer objectUpdateSynchronizer = new GraphTransactionInterceptor.ObjectUpdateSynchronizer(); + + private final List<Integer> outputList = new ArrayList<>(); + private final int MAX_COUNT = 10; + + class CounterThread extends Thread { + String ids[]; + public CounterThread(String id) { + this.ids = new String[1]; + this.ids[0] = id; + } + + public void setIds(String... ids) { + this.ids = ids; + } + + public void run() { + objectUpdateSynchronizer.lockObject(CollectionUtils.arrayToList(ids)); + for (int i = 0; i < MAX_COUNT; i++) { + outputList.add(i); + RandomStringUtils.randomAlphabetic(20); + } + + objectUpdateSynchronizer.releaseLockedObjects(); + } + } + + @BeforeMethod + public void clearOutputList() { + outputList.clear(); + } + + @Test + public void singleThreadRun() throws InterruptedException { + verifyMultipleThreadRun(1); + } + + @Test + public void twoThreadsAccessingDifferntGuids_DoNotSerialize() throws InterruptedException { + CounterThread th[] = getCounterThreads(false, 2); + + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayNotEquals(populateExpectedArrayOutput(2)); + } + + @Test + public void twoThreadsAccessingSameGuid_Serialize() throws InterruptedException { + verifyMultipleThreadRun(2); + } + + @Test + public void severalThreadsAccessingSameGuid_Serialize() throws InterruptedException { + verifyMultipleThreadRun(10); + } + + @Test + public void severalThreadsSequentialAccessingListOfGuids() throws InterruptedException { + CounterThread th[] = getCounterThreads(false, 10); + int i = 0; + th[i++].setIds("1", "2", "3", "4", "5"); + th[i++].setIds("1", "2", "3", "4"); + th[i++].setIds("1", "2", "3"); + th[i++].setIds("1", "2"); + th[i++].setIds("1"); + th[i++].setIds("1", "2"); + th[i++].setIds("1", "2", "3"); + th[i++].setIds("1", "2", "3", "4"); + th[i++].setIds("1", "2", "3", "4", "5"); + th[i++].setIds("1"); + + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayEquals(populateExpectedArrayOutput(th.length)); + } + + @Test + public void severalThreadsNonSequentialAccessingListOfGuids() throws InterruptedException { + CounterThread th[] = getCounterThreads(false, 5); + int i = 0; + th[i++].setIds("2", "1", "3", "4", "5"); + th[i++].setIds("3", "2", "4", "1"); + th[i++].setIds("2", "3", "1"); + th[i++].setIds("1", "2"); + th[i++].setIds("1"); + + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayEquals(populateExpectedArrayOutput(th.length)); + } + + @Test + public void severalThreadsAccessingOverlappingListOfGuids() throws InterruptedException { + CounterThread th[] = getCounterThreads(false, 5); + int i = 0; + th[i++].setIds("1", "2", "3", "4", "5"); + th[i++].setIds("3", "4", "5", "6"); + th[i++].setIds("5", "6", "7"); + th[i++].setIds("7", "8"); + th[i++].setIds("8"); + + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayNotEquals(populateExpectedArrayOutput(th.length)); + } + + + @Test + public void severalThreadsAccessingOverlappingListOfGuids2() throws InterruptedException { + CounterThread th[] = getCounterThreads(false, 3); + int i = 0; + th[i++].setIds("1", "2", "3", "4", "5"); + th[i++].setIds("6", "7", "8", "9"); + th[i++].setIds("4", "5", "6"); + + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayNotEquals(populateExpectedArrayOutput(th.length)); + } + + @Test + public void severalThreadsAccessingOverlappingListOfGuidsEnsuringSerialOutput() throws InterruptedException { + CounterThread th[] = getCounterThreads(false, 5); + int i = 0; + th[i++].setIds("1", "2", "3", "4", "7"); + th[i++].setIds("3", "4", "5", "7"); + th[i++].setIds("5", "6", "7"); + th[i++].setIds("7", "8"); + th[i++].setIds("7"); + + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayEquals(populateExpectedArrayOutput(th.length)); + } + + private void verifyMultipleThreadRun(int limit) throws InterruptedException { + CounterThread[] th = getCounterThreads(limit); + startCounterThreads(th); + waitForThreadsToEnd(th); + assertArrayEquals(populateExpectedArrayOutput(limit)); + } + + private void startCounterThreads(CounterThread[] th) { + for (int i = 0; i < th.length; i++) { + th[i].start(); + } + } + private CounterThread[] getCounterThreads(int limit) { + return getCounterThreads(true, limit); + } + + private CounterThread[] getCounterThreads(boolean sameId, int limit) { + CounterThread th[] = new CounterThread[limit]; + for (Integer i = 0; i < limit; i++) { + th[i] = new CounterThread(sameId ? "1" : i.toString()); + } + return th; + } + + + private void assertArrayEquals(List<Integer> expected) { + assertEquals(outputList.toArray(), expected.toArray()); + } + + private void assertArrayNotEquals(List<Integer> expected) { + assertFalse(ArrayUtils.isEquals(outputList.toArray(), expected)); + } + + private void waitForThreadsToEnd(CounterThread... threads) throws InterruptedException { + for (Thread t : threads) { + t.join(); + } + } + + private List<Integer> populateExpectedArrayOutput(int limit) { + List<Integer> list = new ArrayList<>(); + for (int i = 0; i < limit*MAX_COUNT; i+=MAX_COUNT) { + for (int j = 0; j < MAX_COUNT; j++) { + list.add(j); + } + } + + return list; + } +}