You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by sa...@apache.org on 2019/07/16 03:36:10 UTC

[atlas] branch master updated: ATLAS-3286: Populate the dynamic attribute flag for each AtlasAttribute in each AltasEntityType

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 85f9b50  ATLAS-3286: Populate the dynamic attribute flag for each AtlasAttribute in each AltasEntityType
85f9b50 is described below

commit 85f9b502e883d3d4870dd201c98aee257a3efa1a
Author: Merryle Wang <me...@cloudera.com>
AuthorDate: Mon Jul 15 20:34:24 2019 -0700

    ATLAS-3286: Populate the dynamic attribute flag for each AtlasAttribute in each AltasEntityType
    
    Signed-off-by: Sarath Subramanian <sa...@apache.org>
---
 .../org/apache/atlas/type/AtlasEntityType.java     | 150 ++++++++++++++++++++-
 .../org/apache/atlas/type/AtlasStructType.java     |  18 ++-
 .../java/org/apache/atlas/type/AttributeToken.java |  43 ++++++
 .../java/org/apache/atlas/type/ConstantToken.java  |  38 ++++++
 .../java/org/apache/atlas/type/DependentToken.java |  52 +++++++
 .../java/org/apache/atlas/type/TemplateToken.java  |  27 ++++
 .../org/apache/atlas/type/TestAtlasEntityType.java |  57 ++++++++
 .../graph/v2/AtlasEntityGraphDiscoveryV2.java      |  36 +++++
 .../notification/NotificationHookConsumerIT.java   |  79 ++++++-----
 .../web/integration/EntityJerseyResourceIT.java    |  14 +-
 10 files changed, 466 insertions(+), 48 deletions(-)

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 d9ae9e3..23eaa0a 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
@@ -30,6 +30,7 @@ import org.apache.atlas.utils.AtlasEntityUtil;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.curator.shaded.com.google.common.annotations.VisibleForTesting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,16 +43,21 @@ import java.util.Map;
 import java.util.Set;
 
 
+
 /**
  * class that implements behaviour of an entity-type.
  */
 public class AtlasEntityType extends AtlasStructType {
     private static final Logger LOG = LoggerFactory.getLogger(AtlasEntityType.class);
 
-    private static final String NAME        = "name";
-    private static final String DESCRIPTION = "description";
-    private static final String OWNER       = "owner";
-    private static final String CREATE_TIME = "createTime";
+    private static final String NAME                         = "name";
+    private static final String DESCRIPTION                  = "description";
+    private static final String OWNER                        = "owner";
+    private static final String CREATE_TIME                  = "createTime";
+    private static final String DYN_ATTRIBUTE_PREFIX         = "dynAttribute:";
+    private static final char   DYN_ATTRIBUTE_NAME_SEPARATOR = '.';
+    private static final char   DYN_ATTRIBUTE_OPEN_DELIM     = '{';
+    private static final char   DYN_ATTRIBUTE_CLOSE_DELIM    = '}';
 
     private static final String[] ENTITY_HEADER_ATTRIBUTES = new String[]{NAME, DESCRIPTION, OWNER, CREATE_TIME};
     private static final String   OPTION_SCHEMA_ATTRIBUTES = "schemaAttributes";
@@ -73,6 +79,9 @@ public class AtlasEntityType extends AtlasStructType {
     private boolean                                  isInternalType             = false;
     private Map<String, AtlasAttribute>              headerAttributes           = Collections.emptyMap();
     private Map<String, AtlasAttribute>              minInfoAttributes          = Collections.emptyMap();
+    private List<AtlasAttribute>                     dynAttributes              = Collections.emptyList();
+    private List<AtlasAttribute>                     dynEvalTriggerAttributes   = Collections.emptyList();
+    private Map<String,List<TemplateToken>>          parsedTemplates            = Collections.emptyMap();
 
 
     public AtlasEntityType(AtlasEntityDef entityDef) {
@@ -256,6 +265,10 @@ public class AtlasEntityType extends AtlasStructType {
         }
 
         entityDef.setRelationshipAttributeDefs(Collections.unmodifiableList(relationshipAttrDefs));
+
+        this.parsedTemplates = parseDynAttributeTemplates();
+
+        populateDynFlagsInfo();
     }
 
     public Set<String> getSuperTypes() {
@@ -318,6 +331,18 @@ public class AtlasEntityType extends AtlasStructType {
         return ownedRefAttributes;
     }
 
+    public List<AtlasAttribute> getDynEvalAttributes() { return dynAttributes; }
+
+    @VisibleForTesting
+    public void setDynEvalAttributes(List<AtlasAttribute> dynAttributes) { this.dynAttributes = dynAttributes; }
+
+    public List<AtlasAttribute> getDynEvalTriggerAttributes() { return dynEvalTriggerAttributes; }
+
+    @VisibleForTesting
+    public void setDynEvalTriggerAttributes(List<AtlasAttribute> dynEvalTriggerAttributes) { this.dynEvalTriggerAttributes = dynEvalTriggerAttributes; }
+
+    public Map<String,List<TemplateToken>> getParsedTemplates() { return parsedTemplates; }
+
     public AtlasAttribute getRelationshipAttribute(String attributeName, String relationshipType) {
         final AtlasAttribute        ret;
         Map<String, AtlasAttribute> attributes = relationshipAttributes.get(attributeName);
@@ -651,6 +676,123 @@ public class AtlasEntityType extends AtlasStructType {
         }
     }
 
+    private void populateDynFlagsInfo() {
+        dynAttributes              = new ArrayList<>();
+        dynEvalTriggerAttributes   = new ArrayList<>();
+
+        for (String attributeName : parsedTemplates.keySet()) {
+            AtlasAttribute attribute = getAttribute(attributeName);
+            if (attribute != null) {
+                dynAttributes.add(attribute);
+            }
+        }
+
+        for (List<TemplateToken> parsedTemplate : parsedTemplates.values()) {
+            for (TemplateToken token : parsedTemplate) {
+                // If token is an instance of AttributeToken means that the attribute is of this entity type
+                // so it must be added to the dynEvalTriggerAttributes list
+                if (token instanceof AttributeToken) {
+                    AtlasAttribute attribute = getAttribute(token.getValue());
+
+                    if (attribute != null) {
+                        dynEvalTriggerAttributes.add(attribute);
+                    }
+                }
+            }
+        }
+
+        dynAttributes              = Collections.unmodifiableList(dynAttributes);
+        dynEvalTriggerAttributes   = Collections.unmodifiableList(dynEvalTriggerAttributes);
+
+        for (AtlasAttribute attribute : dynAttributes) {
+            attribute.setIsDynAttribute(true);
+        }
+
+        for (AtlasAttribute attribute : dynEvalTriggerAttributes) {
+            attribute.setIsDynAttributeEvalTrigger(true);
+        }
+    }
+
+    private Map<String, List<TemplateToken>> parseDynAttributeTemplates(){
+        Map<String, List<TemplateToken>> ret = new HashMap<>();
+        Map<String, String> options = entityDef.getOptions();
+        if (options == null || options.size() == 0) {
+            return ret;
+        }
+
+        for (String key : options.keySet()) {
+            if (key.startsWith(DYN_ATTRIBUTE_PREFIX)) {
+                String         attributeName   = key.substring(DYN_ATTRIBUTE_PREFIX.length());
+                AtlasAttribute attribute       = getAttribute(attributeName);
+
+                if (attribute == null) {
+                    LOG.warn("Ignoring {} attribute of {} type as dynamic attribute because attribute does not exist", attributeName, this.getTypeName());
+                    continue;
+                }
+
+                if (!(attribute.getAttributeType() instanceof AtlasBuiltInTypes.AtlasStringType)) {
+                    LOG.warn("Ignoring {} attribute of {} type as dynamic attribute because attribute isn't a string type", attributeName, this.getTypeName());
+                    continue;
+                }
+
+                String              template        = options.get(key);
+                List<TemplateToken> splitTemplate   = templateSplit(template);
+
+                ret.put(attributeName,splitTemplate);
+            }
+        }
+
+        return Collections.unmodifiableMap(ret);
+    }
+
+    // own split function that also designates the right subclass for each token
+    private List<TemplateToken> templateSplit(String template) {
+        List<TemplateToken> ret         = new ArrayList<>();
+        StringBuilder       token       = new StringBuilder();
+        boolean             isInAttrName   = false;
+
+        for (int i = 0; i < template.length(); i++) {
+            char c = template.charAt(i);
+
+            switch (c) {
+                case DYN_ATTRIBUTE_OPEN_DELIM:
+                    isInAttrName = true;
+
+                    if (token.length() > 0) {
+                        ret.add(new ConstantToken(token.toString()));
+                        token.setLength(0);
+                    }
+                    break;
+
+                case DYN_ATTRIBUTE_CLOSE_DELIM:
+                    if (isInAttrName) {
+                        isInAttrName = false;
+
+                        if (token.length() > 0) {
+                            String attrName = token.toString();
+
+                            if (attrName.indexOf(DYN_ATTRIBUTE_NAME_SEPARATOR) != -1) {
+                                ret.add(new DependentToken(token.toString()));
+                            } else {
+                                ret.add(new AttributeToken(token.toString()));
+                            }
+
+                            token.setLength(0);
+                        }
+                    } else {
+                        token.append(c);
+                    }
+                    break;
+
+                default:
+                    token.append(c);
+                    break;
+            }
+        }
+
+        return ret;
+    }
+
     boolean isAssignableFrom(AtlasObjectId objId) {
         boolean ret = AtlasTypeUtil.isValid(objId) && (StringUtils.equals(objId.getTypeName(), getTypeName()) || isSuperTypeOf(objId.getTypeName()));
 
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java b/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java
index 0fe47bd..e7ec3d8 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java
@@ -710,6 +710,9 @@ public class AtlasStructType extends AtlasType {
         private boolean                        isLegacyAttribute;
         private String                         indexFieldName;
 
+        private boolean isDynAttribute            = false;
+        private boolean isDynAttributeEvalTrigger = false;
+
         public AtlasAttribute(AtlasStructType definedInType, AtlasAttributeDef attrDef, AtlasType attributeType, String relationshipName, String relationshipLabel) {
             this.definedInType            = definedInType;
             this.attributeDef             = attrDef;
@@ -746,23 +749,23 @@ public class AtlasStructType extends AtlasType {
             switch (this.attributeType.getTypeCategory()) {
                 case OBJECT_ID_TYPE:
                     isObjectRef = true;
-                break;
+                    break;
 
                 case MAP:
                     AtlasMapType mapType = (AtlasMapType) this.attributeType;
 
                     isObjectRef = mapType.getValueType().getTypeCategory() == OBJECT_ID_TYPE;
-                break;
+                    break;
 
                 case ARRAY:
                     AtlasArrayType arrayType = (AtlasArrayType) this.attributeType;
 
                     isObjectRef = arrayType.getElementType().getTypeCategory() == OBJECT_ID_TYPE;
-                break;
+                    break;
 
                 default:
                     isObjectRef = false;
-                break;
+                    break;
             }
         }
 
@@ -826,6 +829,13 @@ public class AtlasStructType extends AtlasType {
 
         public int getSearchWeight() { return attributeDef.getSearchWeight(); }
 
+        public boolean getIsDynAttribute() { return isDynAttribute; }
+
+        public void setIsDynAttribute(boolean isDynAttribute){ this.isDynAttribute = isDynAttribute; }
+
+        public boolean getIsDynAttributeEvalTrigger() { return isDynAttributeEvalTrigger; }
+
+        public void setIsDynAttributeEvalTrigger(boolean isDynAttributeEvalTrigger) { this.isDynAttributeEvalTrigger = isDynAttributeEvalTrigger; }
 
         public static String getEdgeLabel(String property) {
             return "__" + property;
diff --git a/intg/src/main/java/org/apache/atlas/type/AttributeToken.java b/intg/src/main/java/org/apache/atlas/type/AttributeToken.java
new file mode 100644
index 0000000..658cf86
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/type/AttributeToken.java
@@ -0,0 +1,43 @@
+/**
+ * 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.type;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasEntity;
+
+public class AttributeToken implements TemplateToken {
+    private final String attrName;
+
+    public AttributeToken(String attrName){
+        this.attrName = attrName;
+    }
+
+    @Override
+    public String eval(AtlasEntity entity) throws AtlasBaseException {
+        Object ret = entity.getAttribute(attrName);
+        if (ret == null) {
+            return null;
+        }
+        return ret.toString();
+    }
+
+    @Override
+    public String getValue() {
+        return attrName;
+    }
+}
diff --git a/intg/src/main/java/org/apache/atlas/type/ConstantToken.java b/intg/src/main/java/org/apache/atlas/type/ConstantToken.java
new file mode 100644
index 0000000..5ba54ae
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/type/ConstantToken.java
@@ -0,0 +1,38 @@
+/**
+ * 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.type;
+
+import org.apache.atlas.model.instance.AtlasEntity;
+
+public class ConstantToken implements TemplateToken {
+    private final String constant;
+
+    public ConstantToken(String constant){
+        this.constant = constant;
+    }
+
+    @Override
+    public String eval(AtlasEntity entity) {
+        return constant;
+    }
+
+    @Override
+    public String getValue() {
+        return constant;
+    }
+}
diff --git a/intg/src/main/java/org/apache/atlas/type/DependentToken.java b/intg/src/main/java/org/apache/atlas/type/DependentToken.java
new file mode 100644
index 0000000..c1c7d3d
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/type/DependentToken.java
@@ -0,0 +1,52 @@
+/**
+ * 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.type;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasEntity;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class DependentToken implements TemplateToken {
+    private final String       path;
+    private final List<String> objectPath;
+    private final String       attrName;
+
+    private static final String DYN_ATTRIBUTE_NAME_SEPARATOR = "\\.";
+
+    public DependentToken(String path){
+        List<String> objectPath = new ArrayList<>(Arrays.asList(path.split(DYN_ATTRIBUTE_NAME_SEPARATOR)));
+
+        this.path      = path;
+        this.attrName  = objectPath.remove(objectPath.size() - 1);
+        this.objectPath = Collections.unmodifiableList(objectPath);
+    }
+
+    @Override
+    public String eval(AtlasEntity entity) throws AtlasBaseException {
+        return "TEMP";
+    }
+
+    @Override
+    public String getValue() {
+        return path;
+    }
+}
diff --git a/intg/src/main/java/org/apache/atlas/type/TemplateToken.java b/intg/src/main/java/org/apache/atlas/type/TemplateToken.java
new file mode 100644
index 0000000..fffbc2a
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/type/TemplateToken.java
@@ -0,0 +1,27 @@
+/**
+ * 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.type;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasEntity;
+
+public interface TemplateToken {
+    String eval(AtlasEntity entity) throws AtlasBaseException;
+
+    String getValue();
+}
diff --git a/intg/src/test/java/org/apache/atlas/type/TestAtlasEntityType.java b/intg/src/test/java/org/apache/atlas/type/TestAtlasEntityType.java
index 3c53c02..c114bdf 100644
--- a/intg/src/test/java/org/apache/atlas/type/TestAtlasEntityType.java
+++ b/intg/src/test/java/org/apache/atlas/type/TestAtlasEntityType.java
@@ -42,6 +42,7 @@ public class TestAtlasEntityType {
     private static final String TYPE_COLUMN  = "my_column";
     private static final String ATTR_TABLE   = "table";
     private static final String ATTR_COLUMNS = "columns";
+    private static final String ATTR_OWNER   = "owner";
     private static final String ATTR_NAME    = "name";
 
     private final AtlasEntityType entityType;
@@ -164,6 +165,39 @@ public class TestAtlasEntityType {
     }
 
     @Test
+    public void testDynAttributeFlags() {
+        AtlasTypeRegistry          typeRegistry = new AtlasTypeRegistry();
+        AtlasTransientTypeRegistry ttr          = null;
+        boolean                    commit       = false;
+        List<AtlasEntityDef>       entityDefs   = new ArrayList<>();
+        String                     failureMsg   = null;
+
+        entityDefs.add(createTableEntityDefWithOptions());
+        entityDefs.add(createColumnEntityDef());
+
+        try {
+            ttr = typeRegistry.lockTypeRegistryForUpdate();
+
+            ttr.addTypes(entityDefs);
+            //options are read in the table,
+            AtlasEntityType typeTable  = ttr.getEntityTypeByName(TYPE_TABLE);
+            AtlasEntityType typeColumn = ttr.getEntityTypeByName(TYPE_COLUMN);
+
+            assertTrue(typeTable.getAttribute(ATTR_NAME).getIsDynAttributeEvalTrigger());
+            assertFalse(typeTable.getAttribute(ATTR_NAME).getIsDynAttribute());
+            assertFalse(typeTable.getAttribute(ATTR_OWNER).getIsDynAttributeEvalTrigger());
+            assertTrue(typeTable.getAttribute(ATTR_OWNER).getIsDynAttribute());
+
+            commit = true;
+        } catch (AtlasBaseException excp) {
+            failureMsg = excp.getMessage();
+        } finally {
+            typeRegistry.releaseTypeRegistryForUpdate(ttr, commit);
+        }
+        assertNull(failureMsg, "failed to create types " + TYPE_TABLE + " and " + TYPE_COLUMN);
+    }
+
+    @Test
     public void testConstraintInvalidOwnedRef_InvalidAttributeType() {
         AtlasTypeRegistry          typeRegistry = new AtlasTypeRegistry();
         AtlasTransientTypeRegistry ttr          = null;
@@ -314,6 +348,29 @@ public class TestAtlasEntityType {
         return table;
     }
 
+    private AtlasEntityDef createTableEntityDefWithOptions() {
+        AtlasEntityDef    table       = new AtlasEntityDef(TYPE_TABLE);
+        AtlasAttributeDef attrName    = new AtlasAttributeDef(ATTR_NAME, AtlasBaseTypeDef.ATLAS_TYPE_STRING);
+        AtlasAttributeDef attrColumns = new AtlasAttributeDef(ATTR_COLUMNS, AtlasBaseTypeDef.getArrayTypeName(TYPE_COLUMN));
+        AtlasAttributeDef attrOwner   = new AtlasAttributeDef(ATTR_OWNER, AtlasBaseTypeDef.ATLAS_TYPE_STRING);
+
+        attrColumns.addConstraint(new AtlasConstraintDef(AtlasConstraintDef.CONSTRAINT_TYPE_OWNED_REF));
+
+        table.addAttribute(attrName);
+        table.addAttribute(attrColumns);
+        table.addAttribute(attrOwner);
+
+        Map<String,String> options = new HashMap<>();
+        String             key     = "dynAttribute:" + ATTR_OWNER;
+        String             value   = "{" + ATTR_NAME + "}";
+
+        options.put(key,value);
+
+        table.setOptions(options);
+
+        return table;
+    }
+
     private AtlasEntityDef createTableEntityDefWithOwnedRefOnInvalidType() {
         AtlasEntityDef    table    = new AtlasEntityDef(TYPE_TABLE);
         AtlasAttributeDef attrName = new AtlasAttributeDef(ATTR_NAME, AtlasBaseTypeDef.ATLAS_TYPE_STRING);
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityGraphDiscoveryV2.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityGraphDiscoveryV2.java
index 4ff4206..84bb2a3 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityGraphDiscoveryV2.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityGraphDiscoveryV2.java
@@ -36,6 +36,7 @@ import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
 import org.apache.atlas.type.AtlasType;
 import org.apache.atlas.type.AtlasTypeRegistry;
 import org.apache.atlas.type.AtlasTypeUtil;
+import org.apache.atlas.type.TemplateToken;
 import org.apache.atlas.utils.AtlasEntityUtil;
 import org.apache.atlas.utils.AtlasPerfMetrics.MetricRecorder;
 import org.slf4j.Logger;
@@ -144,6 +145,8 @@ public class AtlasEntityGraphDiscoveryV2 implements EntityGraphDiscovery {
                 throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "found null entity");
             }
 
+            processDynamicAttributes(entity);
+
             walkEntityGraph(entity);
 
             walkedEntities.add(entity.getGuid());
@@ -395,4 +398,37 @@ public class AtlasEntityGraphDiscoveryV2 implements EntityGraphDiscovery {
             discoveryContext.addReferencedByUniqAttribs(objId);
         }
     }
+
+    private void processDynamicAttributes(AtlasEntity entity) throws AtlasBaseException {
+        AtlasEntityType entityType = typeRegistry.getEntityTypeByName(entity.getTypeName());
+
+        for (AtlasAttribute attribute : entityType.getDynEvalAttributes()) {
+            String              attributeName   = attribute.getName();
+            List<TemplateToken> tokens          = entityType.getParsedTemplates().get(attributeName);
+
+            if (tokens == null) {
+                continue;
+            }
+
+            StringBuilder dynAttributeValue = new StringBuilder();
+
+            boolean set = true;
+
+            for (TemplateToken token : tokens) {
+                String evaluated = token.eval(entity);
+                if (evaluated != null) {
+                    dynAttributeValue.append(evaluated);
+                } else {
+                    set = false;
+                    LOG.warn("Attribute {} for {} unable to be generated because of dynamic attribute token {}", attributeName, entityType, token.getValue());
+                    break;
+                }
+
+            }
+
+            if (set) {
+                entity.setAttribute(attributeName,dynAttributeValue.toString());
+            }
+        }
+    }
 }
diff --git a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerIT.java b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerIT.java
index bf7e182..6e24f52 100644
--- a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerIT.java
+++ b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerIT.java
@@ -93,13 +93,15 @@ public class NotificationHookConsumerIT extends BaseResourceIT {
 
     @Test
     public void testCreateEntity() throws Exception {
-        final Referenceable entity = new Referenceable(DATABASE_TYPE_BUILTIN);
-        final String        dbName = "db" + randomString();
+        final Referenceable entity        = new Referenceable(DATABASE_TYPE_BUILTIN);
+        final String        dbName        = "db" + randomString();
+        final String        clusterName   = randomString();
+        final String        qualifiedName = dbName + "@" + clusterName;
 
         entity.set(NAME, dbName);
         entity.set(DESCRIPTION, randomString());
-        entity.set(QUALIFIED_NAME, dbName);
-        entity.set(CLUSTER_NAME, randomString());
+        entity.set(QUALIFIED_NAME, qualifiedName);
+        entity.set(CLUSTER_NAME, clusterName);
 
         sendHookMessage(new EntityCreateRequest(TEST_USER, entity));
 
@@ -122,13 +124,15 @@ public class NotificationHookConsumerIT extends BaseResourceIT {
 
     @Test
     public void testUpdateEntityPartial() throws Exception {
-        final Referenceable entity = new Referenceable(DATABASE_TYPE_BUILTIN);
-        final String        dbName = "db" + randomString();
+        final Referenceable entity        = new Referenceable(DATABASE_TYPE_BUILTIN);
+        final String        dbName        = "db" + randomString();
+        final String        clusterName   = randomString();
+        final String        qualifiedName = dbName + "@" + clusterName;
 
         entity.set(NAME, dbName);
         entity.set(DESCRIPTION, randomString());
-        entity.set(QUALIFIED_NAME, dbName);
-        entity.set(CLUSTER_NAME, randomString());
+        entity.set(QUALIFIED_NAME, qualifiedName);
+        entity.set(CLUSTER_NAME, clusterName);
 
         atlasClientV1.createEntity(entity);
 
@@ -136,70 +140,75 @@ public class NotificationHookConsumerIT extends BaseResourceIT {
 
         newEntity.set("owner", randomString());
 
-        sendHookMessage(new EntityPartialUpdateRequest(TEST_USER, DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName, newEntity));
+        sendHookMessage(new EntityPartialUpdateRequest(TEST_USER, DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, (String) entity.get(QUALIFIED_NAME), newEntity));
 
         waitFor(MAX_WAIT_TIME, new Predicate() {
             @Override
             public boolean evaluate() throws Exception {
-                Referenceable localEntity = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName);
+                Referenceable localEntity = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, qualifiedName);
 
                 return (localEntity.get("owner") != null && localEntity.get("owner").equals(newEntity.get("owner")));
             }
         });
 
         //Its partial update and un-set fields are not updated
-        Referenceable actualEntity = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName);
+        Referenceable actualEntity = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, (String) entity.get(QUALIFIED_NAME));
 
         assertEquals(actualEntity.get(DESCRIPTION), entity.get(DESCRIPTION));
     }
 
     @Test
     public void testUpdatePartialUpdatingQualifiedName() throws Exception {
-        final Referenceable entity = new Referenceable(DATABASE_TYPE_BUILTIN);
-        final String        dbName = "db" + randomString();
+        final Referenceable entity        = new Referenceable(DATABASE_TYPE_BUILTIN);
+        final String        dbName        = "db" + randomString();
+        final String        clusterName   = randomString();
+        final String        qualifiedName = dbName + "@" + clusterName;
 
         entity.set(NAME, dbName);
         entity.set(DESCRIPTION, randomString());
-        entity.set(QUALIFIED_NAME, dbName);
-        entity.set(CLUSTER_NAME, randomString());
+        entity.set(QUALIFIED_NAME, qualifiedName);
+        entity.set(CLUSTER_NAME, clusterName);
 
         atlasClientV1.createEntity(entity);
 
-        final Referenceable newEntity = new Referenceable(DATABASE_TYPE_BUILTIN);
-        final String        newName   = "db" + randomString();
+        final Referenceable newEntity        = new Referenceable(DATABASE_TYPE_BUILTIN);
+        final String        newName          = "db" + randomString();
+        final String        newQualifiedName = newName + "@" + clusterName;
 
-        newEntity.set(QUALIFIED_NAME, newName);
+        newEntity.set(QUALIFIED_NAME, newQualifiedName);
 
-        sendHookMessage(new EntityPartialUpdateRequest(TEST_USER, DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName, newEntity));
+        sendHookMessage(new EntityPartialUpdateRequest(TEST_USER, DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, qualifiedName, newEntity));
 
         waitFor(MAX_WAIT_TIME, new Predicate() {
             @Override
             public boolean evaluate() throws Exception {
-                ArrayNode results = searchByDSL(String.format("%s where qualifiedName='%s'", DATABASE_TYPE_BUILTIN, newName));
+                ArrayNode results = searchByDSL(String.format("%s where qualifiedName='%s'", DATABASE_TYPE_BUILTIN, newQualifiedName));
 
                 return results.size() == 1;
             }
         });
 
         //no entity with the old qualified name
-        ArrayNode results = searchByDSL(String.format("%s where qualifiedName='%s'", DATABASE_TYPE_BUILTIN, dbName));
+        ArrayNode results = searchByDSL(String.format("%s where qualifiedName='%s'", DATABASE_TYPE_BUILTIN, qualifiedName));
 
         assertEquals(results.size(), 0);
     }
 
     @Test
     public void testDeleteByQualifiedName() throws Exception {
-        final Referenceable entity = new Referenceable(DATABASE_TYPE_BUILTIN);
-        final String        dbName = "db" + randomString();
+        final Referenceable entity        = new Referenceable(DATABASE_TYPE_BUILTIN);
+        final String        dbName        = "db" + randomString();
+        final String        clusterName   = randomString();
+        final String        qualifiedName = dbName + "@" + clusterName;
 
         entity.set(NAME, dbName);
         entity.set(DESCRIPTION, randomString());
-        entity.set(QUALIFIED_NAME, dbName);
-        entity.set(CLUSTER_NAME, randomString());
+        entity.set(QUALIFIED_NAME, qualifiedName);
+        entity.set(CLUSTER_NAME, clusterName);
 
         final String dbId = atlasClientV1.createEntity(entity).get(0);
 
-        sendHookMessage(new EntityDeleteRequest(TEST_USER, DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName));
+        sendHookMessage(new EntityDeleteRequest(TEST_USER, DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, qualifiedName));
 
         waitFor(MAX_WAIT_TIME, new Predicate() {
             @Override
@@ -213,23 +222,25 @@ public class NotificationHookConsumerIT extends BaseResourceIT {
 
     @Test
     public void testUpdateEntityFullUpdate() throws Exception {
-        final Referenceable entity = new Referenceable(DATABASE_TYPE_BUILTIN);
-        final String        dbName = "db" + randomString();
+        final Referenceable entity        = new Referenceable(DATABASE_TYPE_BUILTIN);
+        final String        dbName        = "db" + randomString();
+        final String        clusterName   = randomString();
+        final String        qualifiedName = dbName + "@" + clusterName;
 
         entity.set(NAME, dbName);
         entity.set(DESCRIPTION, randomString());
-        entity.set(QUALIFIED_NAME, dbName);
-        entity.set(CLUSTER_NAME, randomString());
+        entity.set(QUALIFIED_NAME, qualifiedName);
+        entity.set(CLUSTER_NAME, clusterName);
 
         atlasClientV1.createEntity(entity);
 
         final Referenceable newEntity = new Referenceable(DATABASE_TYPE_BUILTIN);
 
-        newEntity.set(NAME, randomString());
+        newEntity.set(NAME, dbName);
         newEntity.set(DESCRIPTION, randomString());
         newEntity.set("owner", randomString());
-        newEntity.set(QUALIFIED_NAME, dbName);
-        newEntity.set(CLUSTER_NAME, randomString());
+        newEntity.set(QUALIFIED_NAME, qualifiedName);
+        newEntity.set(CLUSTER_NAME, clusterName);
 
         //updating unique attribute
         sendHookMessage(new EntityUpdateRequest(TEST_USER, newEntity));
@@ -243,7 +254,7 @@ public class NotificationHookConsumerIT extends BaseResourceIT {
             }
         });
 
-        Referenceable actualEntity = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName);
+        Referenceable actualEntity = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, qualifiedName);
 
         assertEquals(actualEntity.get(DESCRIPTION), newEntity.get(DESCRIPTION));
         assertEquals(actualEntity.get("owner"), newEntity.get("owner"));
diff --git a/webapp/src/test/java/org/apache/atlas/web/integration/EntityJerseyResourceIT.java b/webapp/src/test/java/org/apache/atlas/web/integration/EntityJerseyResourceIT.java
index edb4568..70775b3 100755
--- a/webapp/src/test/java/org/apache/atlas/web/integration/EntityJerseyResourceIT.java
+++ b/webapp/src/test/java/org/apache/atlas/web/integration/EntityJerseyResourceIT.java
@@ -277,9 +277,10 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
     public void testGetEntityByAttribute() throws Exception {
         Referenceable db1 = new Referenceable(DATABASE_TYPE_BUILTIN);
         String dbName = randomString();
+        String qualifiedName = dbName + "@cl1";
         db1.set(NAME, dbName);
         db1.set(DESCRIPTION, randomString());
-        db1.set(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME, dbName);
+        db1.set(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME, qualifiedName);
         db1.set("owner", "user1");
         db1.set(CLUSTER_NAME, "cl1");
         db1.set("parameters", Collections.EMPTY_MAP);
@@ -287,9 +288,9 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
         createInstance(db1);
 
         //get entity by attribute
-        Referenceable referenceable = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName);
+        Referenceable referenceable = atlasClientV1.getEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, qualifiedName);
         Assert.assertEquals(referenceable.getTypeName(), DATABASE_TYPE_BUILTIN);
-        Assert.assertEquals(referenceable.get(QUALIFIED_NAME), dbName);
+        Assert.assertEquals(referenceable.get(QUALIFIED_NAME), dbName + "@" + "cl1");
     }
 
     @Test
@@ -1096,11 +1097,12 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
         // Create database entity
         Referenceable db1 = new Referenceable(DATABASE_TYPE_BUILTIN);
         String dbName = randomString();
+        String qualifiedName = dbName + "@cl1";
         db1.set(NAME, dbName);
-        db1.set(QUALIFIED_NAME, dbName);
+        db1.set(QUALIFIED_NAME, qualifiedName);
         db1.set(CLUSTER_NAME, randomString());
         db1.set(DESCRIPTION, randomString());
-        db1.set(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME, dbName);
+        db1.set(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME, qualifiedName);
         db1.set("owner", "user1");
         db1.set(CLUSTER_NAME, "cl1");
         db1.set("parameters", Collections.EMPTY_MAP);
@@ -1108,7 +1110,7 @@ public class EntityJerseyResourceIT extends BaseResourceIT {
         Id db1Id = createInstance(db1);
 
         // Delete the database entity
-        List<String> deletedGuidsList = atlasClientV1.deleteEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, dbName).getDeletedEntities();
+        List<String> deletedGuidsList = atlasClientV1.deleteEntity(DATABASE_TYPE_BUILTIN, QUALIFIED_NAME, qualifiedName).getDeletedEntities();
 
         // Verify that deleteEntities() response has database entity guids
         Assert.assertEquals(deletedGuidsList.size(), 1);