This is an automated email from the ASF dual-hosted git repository.

sarath pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/branch-2.0 by this push:
     new 1d026fa  ATLAS-4522: Updating typedef with new supertype should be 
allowed only if attributes are unique compared to other existing supertypes
1d026fa is described below

commit 1d026fa463bbda95ac78b4a8db377412989077b6
Author: Radhika Kundam <rkun...@cloudera.com>
AuthorDate: Wed Jan 5 17:38:52 2022 -0500

    ATLAS-4522: Updating typedef with new supertype should be allowed only if 
attributes are unique compared to other existing supertypes
    
    Signed-off-by: Sarath Subramanian <sar...@apache.org>
    (cherry picked from commit 1b0dff89e26ff06f7b14456369975fe696f05ebc)
---
 .../main/java/org/apache/atlas/AtlasErrorCode.java |  1 +
 .../org/apache/atlas/type/AtlasEntityType.java     | 18 ++++--
 .../apache/atlas/type/TestAtlasTypeRegistry.java   | 66 ++++++++++++++++++++++
 .../store/graph/v2/EntityGraphRetriever.java       |  2 +
 .../store/graph/v2/AtlasEntityStoreV2Test.java     |  5 +-
 .../web/integration/TypedefsJerseyResourceIT.java  |  3 +-
 .../web/integration/TypesJerseyResourceIT.java     |  3 +-
 7 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java 
b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index 9d11820..c7e6f3a 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -173,6 +173,7 @@ public enum AtlasErrorCode {
     NO_DATA_FOUND(400, "ATLAS-400-00-09B", "No data found in the uploaded 
file"),
     NOT_VALID_FILE(400, "ATLAS-400-00-09C", "Invalid {0} file"),
     ATTRIBUTE_NAME_ALREADY_EXISTS_IN_PARENT_TYPE(400, "ATLAS-400-00-09D", 
"Invalid attribute name: {0}.{1}. Attribute already exists in parent type: 
{2}"),
+    ATTRIBUTE_NAME_ALREADY_EXISTS_IN_ANOTHER_PARENT_TYPE(400, 
"ATLAS-400-00-09E", "Invalid attribute name: {0}.{1}. Attribute already exists 
in another parent type: {2}"),
     UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to 
perform {1}"),
 
     // All Not found enums go here
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java 
b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
index 6d83599..76bee36 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
@@ -843,9 +843,10 @@ public class AtlasEntityType extends AtlasStructType {
     private void getTypeHierarchyInfo(AtlasTypeRegistry typeRegistry,
                                       Set<String> allSuperTypeNames,
                                       Map<String, AtlasAttribute> 
allAttributes) throws AtlasBaseException {
-        List<String> visitedTypes = new ArrayList<>();
+        List<String>        visitedTypes             = new ArrayList<>();
+        Map<String, String> attributeToEntityNameMap = new HashMap<>();
 
-        collectTypeHierarchyInfo(typeRegistry, allSuperTypeNames, 
allAttributes, visitedTypes);
+        collectTypeHierarchyInfo(typeRegistry, allSuperTypeNames, 
allAttributes, attributeToEntityNameMap, visitedTypes);
     }
 
 
@@ -858,6 +859,7 @@ public class AtlasEntityType extends AtlasStructType {
     private void collectTypeHierarchyInfo(AtlasTypeRegistry typeRegistry,
                                           Set<String> allSuperTypeNames,
                                           Map<String, AtlasAttribute> 
allAttributes,
+                                          Map<String, String> 
attributeToEntityNameMap,
                                           List<String> visitedTypes) throws 
AtlasBaseException {
         if (visitedTypes.contains(entityDef.getName())) {
             throw new AtlasBaseException(AtlasErrorCode.CIRCULAR_REFERENCE, 
entityDef.getName(),
@@ -870,7 +872,7 @@ public class AtlasEntityType extends AtlasStructType {
                 AtlasEntityType superType = 
typeRegistry.getEntityTypeByName(superTypeName);
 
                 if (superType != null) {
-                    superType.collectTypeHierarchyInfo(typeRegistry, 
allSuperTypeNames, allAttributes, visitedTypes);
+                    superType.collectTypeHierarchyInfo(typeRegistry, 
allSuperTypeNames, allAttributes, attributeToEntityNameMap, visitedTypes);
                 }
             }
             visitedTypes.remove(entityDef.getName());
@@ -879,9 +881,15 @@ public class AtlasEntityType extends AtlasStructType {
 
         if (CollectionUtils.isNotEmpty(entityDef.getAttributeDefs())) {
             for (AtlasAttributeDef attributeDef : 
entityDef.getAttributeDefs()) {
+                AtlasType type          = 
typeRegistry.getType(attributeDef.getTypeName());
+                String    attributeName = attributeDef.getName();
 
-                AtlasType type = 
typeRegistry.getType(attributeDef.getTypeName());
-                allAttributes.put(attributeDef.getName(), new 
AtlasAttribute(this, attributeDef, type));
+                if (attributeToEntityNameMap.containsKey(attributeName) && 
!attributeToEntityNameMap.get(attributeName).equals(entityDef.getName())) {
+                    throw new 
AtlasBaseException(AtlasErrorCode.ATTRIBUTE_NAME_ALREADY_EXISTS_IN_ANOTHER_PARENT_TYPE,
 entityDef.getName(), attributeName, 
attributeToEntityNameMap.get(attributeName));
+                }
+
+                allAttributes.put(attributeName, new AtlasAttribute(this, 
attributeDef, type));
+                attributeToEntityNameMap.put(attributeName, 
entityDef.getName());
             }
         }
     }
diff --git 
a/intg/src/test/java/org/apache/atlas/type/TestAtlasTypeRegistry.java 
b/intg/src/test/java/org/apache/atlas/type/TestAtlasTypeRegistry.java
index 3b55a63..85d041b 100644
--- a/intg/src/test/java/org/apache/atlas/type/TestAtlasTypeRegistry.java
+++ b/intg/src/test/java/org/apache/atlas/type/TestAtlasTypeRegistry.java
@@ -593,6 +593,72 @@ public class TestAtlasTypeRegistry {
         validateAllSubTypes(typeRegistry, "L1", new HashSet<String>());
     }
 
+    /* create 2 entity types: L0 and L1, with L0 as superType of L1
+     * Create entity type L2 with same attribute as in L0.
+     * Add L2 as superType of L1 - this should fail as "attr1" already exists 
in other supertype L0
+     * verify that after the update failure, the registry still has correct 
super-type/sub-type information for L1
+     */
+    @Test
+    public void testRegistryValiditySuperTypesUpdateWithExistingAttribute() 
throws AtlasBaseException {
+        AtlasEntityDef entL0 = new AtlasEntityDef("L0");
+        AtlasEntityDef entL1 = new AtlasEntityDef("L1");
+        AtlasEntityDef entL2 = new AtlasEntityDef("L2");
+
+        entL1.addSuperType(entL0.getName());
+
+        entL0.addAttribute(new AtlasAttributeDef("attr1", 
AtlasBaseTypeDef.ATLAS_TYPE_INT));
+        entL1.addAttribute(new AtlasAttributeDef("attr2", 
AtlasBaseTypeDef.ATLAS_TYPE_INT));
+        entL2.addAttribute(new AtlasAttributeDef("attr1", 
AtlasBaseTypeDef.ATLAS_TYPE_INT));
+
+        AtlasTypesDef typesDef = new AtlasTypesDef();
+
+        typesDef.getEntityDefs().add(entL0);
+        typesDef.getEntityDefs().add(entL1);
+        typesDef.getEntityDefs().add(entL2);
+
+        AtlasTypeRegistry          typeRegistry = new AtlasTypeRegistry();
+        AtlasTransientTypeRegistry ttr          = null;
+        boolean                    commit       = false;
+        String                     failureMsg   = null;
+
+        try {
+            ttr = typeRegistry.lockTypeRegistryForUpdate();
+
+            ttr.addTypes(typesDef);
+
+            commit = true;
+        } catch (AtlasBaseException excp) {
+            failureMsg = excp.getMessage();
+        } finally {
+            typeRegistry.releaseTypeRegistryForUpdate(ttr, commit);
+        }
+        assertNull(failureMsg);
+
+        validateAllSuperTypes(typeRegistry, "L1", new 
HashSet<>(Arrays.asList("L0")));
+        validateAllSubTypes(typeRegistry, "L1", new HashSet<String>());
+
+        //Add L2 as supertype for L1
+        entL1.addSuperType(entL2.getName());
+
+        try {
+            commit = false;
+
+            ttr = typeRegistry.lockTypeRegistryForUpdate();
+
+            ttr.updateTypes(typesDef);
+
+            commit = true;
+        } catch (AtlasBaseException excp) {
+            failureMsg = excp.getMessage();
+        } finally {
+            typeRegistry.releaseTypeRegistryForUpdate(ttr, commit);
+        }
+        assertNotNull(failureMsg);
+
+        validateAllSuperTypes(typeRegistry, "L1", new 
HashSet<>(Arrays.asList("L0")));
+        validateAllSubTypes(typeRegistry, "L1", new HashSet<String>());
+    }
+
     private boolean addType(AtlasTypeRegistry typeRegistry, AtlasBaseTypeDef 
typeDef) {
         boolean                    ret = false;
         AtlasTransientTypeRegistry ttr = null;
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphRetriever.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphRetriever.java
index 7948475..9671df9 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphRetriever.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphRetriever.java
@@ -741,6 +741,8 @@ public class EntityGraphRetriever {
 
                 if (attrValue != null) {
                     ret.setAttribute(headerAttribute.getName(), attrValue);
+                } else {
+                    ret.setAttribute(headerAttribute.getName(), 
StringUtils.EMPTY);
                 }
             }
 
diff --git 
a/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2Test.java
 
b/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2Test.java
index 9aced8e..1489b27 100644
--- 
a/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2Test.java
+++ 
b/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2Test.java
@@ -42,6 +42,8 @@ import org.apache.atlas.model.typedef.AtlasTypesDef;
 import org.apache.atlas.type.AtlasTypeUtil;
 import org.apache.atlas.util.FileUtils;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -537,7 +539,8 @@ public class AtlasEntityStoreV2Test extends 
AtlasEntityTestBase {
         init();
         response = entityStore.createOrUpdate(new 
AtlasEntityStream(entitiesInfo), false);
         updatedTable = response.getFirstUpdatedEntityByTypeName(TABLE_TYPE);
-        Assert.assertNull(updatedTable.getAttribute("description"));
+        Object updatedDescription = 
ObjectUtils.defaultIfNull(updatedTable.getAttribute("description"), 
StringUtils.EMPTY);
+        Assert.assertTrue(StringUtils.isEmpty(updatedDescription.toString()));
         validateEntity(entitiesInfo, getEntityFromStore(updatedTable));
     }
 
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/integration/TypedefsJerseyResourceIT.java
 
b/webapp/src/test/java/org/apache/atlas/web/integration/TypedefsJerseyResourceIT.java
index 9506c62..d1aea9d 100644
--- 
a/webapp/src/test/java/org/apache/atlas/web/integration/TypedefsJerseyResourceIT.java
+++ 
b/webapp/src/test/java/org/apache/atlas/web/integration/TypedefsJerseyResourceIT.java
@@ -338,9 +338,10 @@ public class TypedefsJerseyResourceIT extends 
BaseResourceIT {
     @Test
     public void testListTypesByFilter() throws Exception {
         AtlasAttributeDef attr = AtlasTypeUtil.createOptionalAttrDef("attr", 
"string");
+        AtlasAttributeDef attr1 = AtlasTypeUtil.createOptionalAttrDef("attr1", 
"string");
         AtlasEntityDef classDefA = AtlasTypeUtil.createClassTypeDef("A" + 
randomString(), Collections.<String>emptySet(), attr);
         AtlasEntityDef classDefA1 = AtlasTypeUtil.createClassTypeDef("A1" + 
randomString(), Collections.singleton(classDefA.getName()));
-        AtlasEntityDef classDefB = AtlasTypeUtil.createClassTypeDef("B" + 
randomString(), Collections.<String>emptySet(), attr);
+        AtlasEntityDef classDefB = AtlasTypeUtil.createClassTypeDef("B" + 
randomString(), Collections.<String>emptySet(), attr1);
         AtlasEntityDef classDefC = AtlasTypeUtil.createClassTypeDef("C" + 
randomString(), new HashSet<>(Arrays.asList(classDefB.getName(), 
classDefA.getName())));
 
         AtlasTypesDef atlasTypesDef = new AtlasTypesDef();
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/integration/TypesJerseyResourceIT.java
 
b/webapp/src/test/java/org/apache/atlas/web/integration/TypesJerseyResourceIT.java
index 6a0bbec..885fe85 100755
--- 
a/webapp/src/test/java/org/apache/atlas/web/integration/TypesJerseyResourceIT.java
+++ 
b/webapp/src/test/java/org/apache/atlas/web/integration/TypesJerseyResourceIT.java
@@ -211,6 +211,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
     @Test
     public void testListTypesByFilter() throws Exception {
         AttributeDefinition attr = TypesUtil.createOptionalAttrDef("attr", 
AtlasBaseTypeDef.ATLAS_TYPE_STRING);
+        AttributeDefinition attr1 = TypesUtil.createOptionalAttrDef("attr1", 
AtlasBaseTypeDef.ATLAS_TYPE_STRING);
 
         ClassTypeDefinition classTypeDef = TypesUtil.createClassTypeDef("A" + 
randomString(), null, Collections.emptySet(), attr);
         TypesDef typesDef = new TypesDef(Collections.emptyList(), 
Collections.emptyList(), Collections.emptyList(), 
Collections.singletonList(classTypeDef));
@@ -220,7 +221,7 @@ public class TypesJerseyResourceIT extends BaseResourceIT {
         typesDef = new TypesDef(Collections.emptyList(), 
Collections.emptyList(), Collections.emptyList(), 
Collections.singletonList(classTypeDef));
         String a1 = createType(AtlasType.toV1Json(typesDef)).get(0);
 
-        classTypeDef = TypesUtil.createClassTypeDef("B" + randomString(), 
null, Collections.<String>emptySet(), attr);
+        classTypeDef = TypesUtil.createClassTypeDef("B" + randomString(), 
null, Collections.<String>emptySet(), attr1);
         typesDef = new TypesDef(Collections.emptyList(), 
Collections.emptyList(), Collections.emptyList(), 
Collections.singletonList(classTypeDef));
         String b = createType(AtlasType.toV1Json(typesDef)).get(0);
 

Reply via email to