Repository: atlas
Updated Branches:
  refs/heads/master e0ac86961 -> 54d623c31


ATLAS-2807: Re-evaluate classification propagation during entity delete


Project: http://git-wip-us.apache.org/repos/asf/atlas/repo
Commit: http://git-wip-us.apache.org/repos/asf/atlas/commit/54d623c3
Tree: http://git-wip-us.apache.org/repos/asf/atlas/tree/54d623c3
Diff: http://git-wip-us.apache.org/repos/asf/atlas/diff/54d623c3

Branch: refs/heads/master
Commit: 54d623c31f5d3fb8ff40b6ef8f25b99449aecb63
Parents: e0ac869
Author: Sarath Subramanian <[email protected]>
Authored: Fri Aug 10 14:51:48 2018 -0700
Committer: Sarath Subramanian <[email protected]>
Committed: Fri Aug 10 14:51:48 2018 -0700

----------------------------------------------------------------------
 .../org/apache/atlas/repository/Constants.java  |   1 +
 .../java/org/apache/atlas/AtlasErrorCode.java   |   4 +-
 .../model/instance/AtlasClassification.java     |  15 ++-
 .../atlas/repository/graph/GraphHelper.java     |  43 ++++++++
 .../store/graph/AtlasEntityStore.java           |   6 +-
 .../store/graph/v1/DeleteHandlerV1.java         |  56 ++++++++--
 .../store/graph/v1/HardDeleteHandlerV1.java     |   3 -
 .../store/graph/v1/SoftDeleteHandlerV1.java     |   3 -
 .../store/graph/v2/AtlasEntityStoreV2.java      |  23 ++--
 .../store/graph/v2/EntityGraphMapper.java       | 108 ++++++++++++-------
 .../store/graph/v2/EntityGraphRetriever.java    |   2 +
 .../store/graph/v2/AtlasEntityStoreV2Test.java  |  10 +-
 .../ClassificationPropagationTest.java          |  88 +++++++++++++--
 .../test/resources/tag-propagation-data-1.zip   | Bin 0 -> 23480 bytes
 .../atlas/web/resources/EntityResource.java     |   2 +-
 .../org/apache/atlas/web/rest/EntityREST.java   |  10 +-
 .../atlas/web/adapters/TestEntityREST.java      |   9 +-
 17 files changed, 302 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/common/src/main/java/org/apache/atlas/repository/Constants.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/atlas/repository/Constants.java 
b/common/src/main/java/org/apache/atlas/repository/Constants.java
index 7988d1d..d63b376 100644
--- a/common/src/main/java/org/apache/atlas/repository/Constants.java
+++ b/common/src/main/java/org/apache/atlas/repository/Constants.java
@@ -134,6 +134,7 @@ public final class Constants {
     public static final String TEMP_STRUCT_NAME_PREFIX       = 
"__tempQueryResultStruct";
 
     public static final String CLASSIFICATION_ENTITY_GUID                     
= INTERNAL_PROPERTY_KEY_PREFIX + "entityGuid";
+    public static final String CLASSIFICATION_ENTITY_STATUS                   
= INTERNAL_PROPERTY_KEY_PREFIX + "entityStatus";
     public static final String CLASSIFICATION_VALIDITY_PERIODS_KEY            
= INTERNAL_PROPERTY_KEY_PREFIX + "validityPeriods";
     public static final String CLASSIFICATION_VERTEX_PROPAGATE_KEY            
= INTERNAL_PROPERTY_KEY_PREFIX + "propagate";
     public static final String CLASSIFICATION_VERTEX_NAME_KEY                 
= TYPE_NAME_PROPERTY_KEY;

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java 
b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index 328b767..5f901ef 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -128,7 +128,7 @@ public enum AtlasErrorCode {
     CLASSIFICATION_NOT_ASSOCIATED_WITH_ENTITY(400, "ATLAS-400-00-06D", 
"Classification {0} is not associated with entity"),
     UNKNOWN_GLOSSARY_TERM(400, "ATLAS-400-00-06E", "{0}: Unknown/invalid 
glossary term"),
     INVALID_CLASSIFICATION_PARAMS(400, "ATLAS-400-00-06F", "Invalid 
classification parameters passed for {0} operation for entity: {1}"),
-    PROPAGATED_CLASSIFICATION_NOT_ASSOCIATED_WITH_ENTITY(400, 
"ATLAS-400-00-070", "Propagated classification {0} is not associated with 
entity"),
+    PROPAGATED_CLASSIFICATION_NOT_ASSOCIATED_WITH_ENTITY(400, 
"ATLAS-400-00-070", "Propagated classification {0} is not associated with 
entity {2}, it is associated with entity {1}"),
     INVALID_BLOCKED_PROPAGATED_CLASSIFICATION(400, "ATLAS-400-00-071", 
"Invalid propagated classification: {0} with entityGuid: {1} added to blocked 
propagated classifications."),
     MISSING_MANDATORY_ANCHOR(400, "ATLAS-400-00-072", "Mandatory anchor 
attribute is missing"),
     MISSING_MANDATORY_QUALIFIED_NAME(400, "ATLAS-400-00-073", "Mandatory 
qualifiedName attribute is missing"),
@@ -152,6 +152,7 @@ public enum AtlasErrorCode {
     INVALID_TIMEBOUNDRY_START_TIME(400, "ATLAS-400-00-87B", "Invalid startTime 
{0}"),
     INVALID_TIMEBOUNDRY_END_TIME(400, "ATLAS-400-00-87C", "Invalid endTime 
{0}"),
     INVALID_TIMEBOUNDRY_DATERANGE(400, "ATLAS-400-00-87D", "Invalid dateRange: 
startTime {0} must be before endTime {1}"),
+    PROPAGATED_CLASSIFICATION_REMOVAL_NOT_SUPPORTED(400, "ATLAS-400-00-87E", 
"Removal of classification {0}, which is propagated from entity {1}, is not 
supported"),
 
     UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to 
perform {1}"),
 
@@ -174,6 +175,7 @@ public enum AtlasErrorCode {
     INVALID_ENTITY_GUID_FOR_CLASSIFICATION_UPDATE(404, "ATLAS-404-00-010", 
"Updating entityGuid of classification is not allowed."),
     INSTANCE_GUID_NOT_DATASET(404, "ATLAS-404-00-011", "Given instance guid 
{0} is not a dataset"),
     INSTANCE_GUID_DELETED(404, "ATLAS-404-00-012", "Given instance guid {0} 
has been deleted"),
+    NO_PROPAGATED_CLASSIFICATIONS_FOUND_FOR_ENTITY(404, "ATLAS-404-00-013", 
"No propagated classifications associated with entity: {0}"),
 
     // All data conflict errors go here
     TYPE_ALREADY_EXISTS(409, "ATLAS-409-00-001", "Given type {0} already 
exists"),

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/intg/src/main/java/org/apache/atlas/model/instance/AtlasClassification.java
----------------------------------------------------------------------
diff --git 
a/intg/src/main/java/org/apache/atlas/model/instance/AtlasClassification.java 
b/intg/src/main/java/org/apache/atlas/model/instance/AtlasClassification.java
index 3132379..23ff5d2 100644
--- 
a/intg/src/main/java/org/apache/atlas/model/instance/AtlasClassification.java
+++ 
b/intg/src/main/java/org/apache/atlas/model/instance/AtlasClassification.java
@@ -37,6 +37,7 @@ import javax.xml.bind.annotation.XmlSeeAlso;
 import org.apache.atlas.model.PList;
 import org.apache.atlas.model.SearchFilter.SortType;
 import org.apache.atlas.model.TimeBoundary;
+import org.apache.atlas.model.instance.AtlasEntity.Status;
 
 import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
 import static 
com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.PUBLIC_ONLY;
@@ -54,6 +55,7 @@ public class AtlasClassification extends AtlasStruct 
implements Serializable {
     private static final long serialVersionUID = 1L;
 
     private String             entityGuid      = null;
+    private Status             entityStatus    = Status.ACTIVE;
     private Boolean            propagate       = null;
     private List<TimeBoundary> validityPeriods = null;
 
@@ -82,6 +84,7 @@ public class AtlasClassification extends AtlasStruct 
implements Serializable {
             setTypeName(other.getTypeName());
             setAttributes(other.getAttributes());
             setEntityGuid(other.getEntityGuid());
+            setEntityStatus(other.getEntityStatus());
             setPropagate(other.isPropagate());
             setValidityPeriods(other.getValidityPeriods());
         }
@@ -111,6 +114,14 @@ public class AtlasClassification extends AtlasStruct 
implements Serializable {
         this.validityPeriods = validityPeriods;
     }
 
+    public Status getEntityStatus() {
+        return entityStatus;
+    }
+
+    public void setEntityStatus(Status entityStatus) {
+        this.entityStatus = entityStatus;
+    }
+
     @JsonIgnore
     public void addValityPeriod(TimeBoundary validityPeriod) {
         List<TimeBoundary> vpList = this.validityPeriods;
@@ -132,12 +143,13 @@ public class AtlasClassification extends AtlasStruct 
implements Serializable {
         AtlasClassification that = (AtlasClassification) o;
         return Objects.equals(propagate, that.propagate) &&
                Objects.equals(entityGuid, that.entityGuid) &&
+               entityStatus == that.entityStatus &&
                Objects.equals(validityPeriods, that.validityPeriods);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), entityGuid, propagate);
+        return Objects.hash(super.hashCode(), entityGuid, entityStatus, 
propagate);
     }
 
     @Override
@@ -145,6 +157,7 @@ public class AtlasClassification extends AtlasStruct 
implements Serializable {
         final StringBuilder sb = new StringBuilder("AtlasClassification{");
         super.toString(sb);
         sb.append("entityGuid='").append(entityGuid).append('\'');
+        sb.append(", entityStatus=").append(entityStatus);
         sb.append(", propagate=").append(propagate);
         sb.append(", validityPeriods=").append(validityPeriods);
         sb.append('}');

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java 
b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
index d328873..6422399 100755
--- 
a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
@@ -486,6 +486,24 @@ public final class GraphHelper {
         return ret;
     }
 
+    public static List<AtlasEdge> getIncomingClassificationEdges(AtlasVertex 
classificationVertex) {
+        List<AtlasEdge> ret                = new ArrayList<>();
+        String          classificationName = getTypeName(classificationVertex);
+        Iterable        edges              = 
classificationVertex.query().direction(AtlasEdgeDirection.IN).label(CLASSIFICATION_LABEL)
+                                                                 
.has(CLASSIFICATION_EDGE_NAME_PROPERTY_KEY, classificationName).edges();
+        if (edges != null) {
+            Iterator<AtlasEdge> iterator = edges.iterator();
+
+            while (iterator.hasNext()) {
+                AtlasEdge edge = iterator.next();
+
+                ret.add(edge);
+            }
+        }
+
+        return ret;
+    }
+
     public static List<AtlasVertex> getAllPropagatedEntityVertices(AtlasVertex 
classificationVertex) {
         List<AtlasVertex> ret = new ArrayList<>();
 
@@ -980,6 +998,10 @@ public final class GraphHelper {
         return getTraitNames(entityVertex, false);
     }
 
+    public static List<String> getPropagatedTraitNames(AtlasVertex 
entityVertex) {
+        return getTraitNames(entityVertex, true);
+    }
+
     public static List<String> getAllTraitNames(AtlasVertex entityVertex) {
         return getTraitNames(entityVertex, null);
     }
@@ -1198,6 +1220,21 @@ public final class GraphHelper {
         return ret;
     }
 
+    public static boolean isClassificationEdge(AtlasEdge edge) {
+        boolean ret = false;
+
+        if (edge != null) {
+            String  edgeLabel    = edge.getLabel();
+            Boolean isPropagated = 
edge.getProperty(Constants.CLASSIFICATION_EDGE_IS_PROPAGATED_PROPERTY_KEY, 
Boolean.class);
+
+            if (edgeLabel != null && isPropagated != null) {
+                ret = edgeLabel.equals(CLASSIFICATION_LABEL) && !isPropagated;
+            }
+        }
+
+        return ret;
+    }
+
     public static List<String> getBlockedClassificationIds(AtlasEdge edge) {
         List<String> ret = null;
 
@@ -1216,6 +1253,12 @@ public final class GraphHelper {
         return (propagateTags == null) ? null : 
PropagateTags.valueOf(propagateTags);
     }
 
+    public static Status getClassificationEntityStatus(AtlasElement element) {
+        String status = 
element.getProperty(Constants.CLASSIFICATION_ENTITY_STATUS, String.class);
+
+        return (status == null) ? null : Status.valueOf(status);
+    }
+
     //Added conditions in fetching system attributes to handle test failures 
in GremlinTest where these properties are not set
     public static String getCreatedByAsString(AtlasElement element){
         return element.getProperty(Constants.CREATED_BY_KEY, String.class);

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
index c6be406..e6f35fa 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
@@ -197,9 +197,11 @@ public interface AtlasEntityStore {
     void addClassification(List<String> guids, AtlasClassification 
classification) throws AtlasBaseException;
 
     /**
-     * Delete classification(s)
+     * Delete classification
      */
-    void deleteClassifications(String guid, List<String> classificationNames) 
throws AtlasBaseException;
+    void deleteClassification(String guid, String classificationName) throws 
AtlasBaseException;
+
+    void deleteClassification(String guid, String classificationName, String 
associatedEntityGuid) throws AtlasBaseException;
 
     List<AtlasClassification> getClassifications(String guid) throws 
AtlasBaseException;
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
index 4a0924bb..5aa4027 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
@@ -59,6 +59,7 @@ import static 
org.apache.atlas.model.instance.AtlasEntity.Status.ACTIVE;
 import static org.apache.atlas.model.instance.AtlasEntity.Status.DELETED;
 import static 
org.apache.atlas.model.typedef.AtlasRelationshipDef.PropagateTags.ONE_TO_TWO;
 import static 
org.apache.atlas.repository.Constants.CLASSIFICATION_EDGE_NAME_PROPERTY_KEY;
+import static 
org.apache.atlas.repository.Constants.CLASSIFICATION_ENTITY_STATUS;
 import static org.apache.atlas.repository.Constants.CLASSIFICATION_LABEL;
 import static 
org.apache.atlas.repository.Constants.PROPAGATED_TRAIT_NAMES_PROPERTY_KEY;
 import static 
org.apache.atlas.repository.Constants.RELATIONSHIP_GUID_PROPERTY_KEY;
@@ -277,7 +278,8 @@ public abstract class DeleteHandlerV1 {
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Processing delete for typeCategory={}, isOwned={}", 
typeCategory, isOwned);
             }
-            //If the vertex is of type struct/trait, delete the edge and then 
the reference vertex as the vertex is not shared by any other entities.
+            //If the vertex is of type struct delete the edge and then the 
reference vertex as the vertex is not shared by any other entities.
+            //If the vertex is of type classification, delete the edge and 
then the reference vertex only if the vertex is not shared by any other 
propagated entities.
             //If the vertex is of type class, and its composite attribute, 
this reference vertex' lifecycle is controlled
             //through this delete, hence delete the edge and the reference 
vertex.
             AtlasVertex vertexForDelete = edge.getInVertex();
@@ -576,6 +578,37 @@ public abstract class DeleteHandlerV1 {
         }
     }
 
+    public void deletePropagatedClassification(AtlasVertex entityVertex, 
String classificationName, String associatedEntityGuid) throws 
AtlasBaseException {
+        AtlasEdge propagatedEdge = 
getPropagatedClassificationEdge(entityVertex, classificationName, 
associatedEntityGuid);
+
+        if (propagatedEdge == null) {
+            throw new 
AtlasBaseException(AtlasErrorCode.PROPAGATED_CLASSIFICATION_NOT_ASSOCIATED_WITH_ENTITY,
 classificationName, associatedEntityGuid, getGuid(entityVertex));
+        }
+
+        AtlasVertex classificationVertex = propagatedEdge.getInVertex();
+
+        // do not remove propagated classification with ACTIVE associated 
entity
+        if (getClassificationEntityStatus(classificationVertex) == ACTIVE) {
+            throw new 
AtlasBaseException(AtlasErrorCode.PROPAGATED_CLASSIFICATION_REMOVAL_NOT_SUPPORTED,
 classificationName, associatedEntityGuid);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Removing propagated classification: [{} - 
associatedEntityGuid: {}] from: [{}][{}] with edge label: [{}]",
+                       classificationName, associatedEntityGuid, 
getTypeName(entityVertex), getGuid(entityVertex), CLASSIFICATION_LABEL);
+        }
+
+        AtlasClassification classification = 
entityRetriever.toAtlasClassification(classificationVertex);
+
+        // delete classification edge
+        deletePropagatedEdge(propagatedEdge);
+
+        // delete classification vertex
+        deleteClassificationVertex(classificationVertex, true);
+
+        // record remove propagation details to send notifications at the end
+        RequestContext.get().recordRemovedPropagation(getGuid(entityVertex), 
classification);
+    }
+
     public void deletePropagatedEdge(AtlasEdge edge) throws AtlasBaseException 
{
         String      classificationName = AtlasGraphUtilsV2.getProperty(edge, 
CLASSIFICATION_EDGE_NAME_PROPERTY_KEY, String.class);
         AtlasVertex entityVertex       = edge.getOutVertex();
@@ -616,6 +649,12 @@ public abstract class DeleteHandlerV1 {
             }
         }
 
+        if (isClassificationEdge(edge)) {
+            AtlasVertex classificationVertex = edge.getInVertex();
+
+            AtlasGraphUtilsV2.setProperty(classificationVertex, 
CLASSIFICATION_ENTITY_STATUS, DELETED.name());
+        }
+
         deleteEdge(edge, force);
     }
 
@@ -872,12 +911,17 @@ public abstract class DeleteHandlerV1 {
         _deleteVertex(instanceVertex, force);
     }
 
-    protected void deleteClassificationVertex(AtlasVertex 
classificationVertex, boolean force) {
+    public void deleteClassificationVertex(AtlasVertex classificationVertex, 
boolean force) {
         if (LOG.isDebugEnabled()) {
             LOG.debug("Deleting classification vertex", 
string(classificationVertex));
         }
 
-        _deleteVertex(classificationVertex, force);
+        List<AtlasEdge> incomingClassificationEdges = 
getIncomingClassificationEdges(classificationVertex);
+
+        // delete classification vertex only if it has no more entity 
references (direct or propagated)
+        if (CollectionUtils.isEmpty(incomingClassificationEdges)) {
+            _deleteVertex(classificationVertex, force);
+        }
     }
 
     private boolean isInternalType(final AtlasVertex instanceVertex) {
@@ -905,13 +949,9 @@ public abstract class DeleteHandlerV1 {
      * @throws AtlasException
      */
     private void deleteAllClassifications(AtlasVertex instanceVertex) throws 
AtlasBaseException {
-        List<AtlasEdge> classificationEdges = 
getClassificationEdges(instanceVertex);
+        List<AtlasEdge> classificationEdges = 
getAllClassificationEdges(instanceVertex);
 
         for (AtlasEdge edge : classificationEdges) {
-            AtlasVertex classificationVertex = edge.getInVertex();
-
-            removeTagPropagation(classificationVertex);
-
             deleteEdgeReference(edge, CLASSIFICATION, false, false, 
instanceVertex);
         }
     }

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/HardDeleteHandlerV1.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/HardDeleteHandlerV1.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/HardDeleteHandlerV1.java
index a95e689..edf1eed 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/HardDeleteHandlerV1.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/HardDeleteHandlerV1.java
@@ -52,9 +52,6 @@ public class HardDeleteHandlerV1 extends DeleteHandlerV1 {
             LOG.debug("==> HardDeleteHandlerV1.deleteEdge({}, {})", 
GraphHelper.string(edge), force);
         }
 
-        // re-evaluate tag propagation
-        removeTagPropagation(edge);
-
         graphHelper.removeEdge(edge);
     }
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/SoftDeleteHandlerV1.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/SoftDeleteHandlerV1.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/SoftDeleteHandlerV1.java
index 41e65d4..d9cf2e1 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/SoftDeleteHandlerV1.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/SoftDeleteHandlerV1.java
@@ -69,9 +69,6 @@ public class SoftDeleteHandlerV1 extends DeleteHandlerV1 {
             LOG.debug("==> SoftDeleteHandlerV1.deleteEdge({}, 
{})",GraphHelper.string(edge), force);
         }
 
-        // re-evaluate tag propagation
-        removeTagPropagation(edge);
-
         if (force) {
             graphHelper.removeEdge(edge);
         } else {

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
index 28a26ab..6e2a03f 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
@@ -539,27 +539,36 @@ public class AtlasEntityStoreV2 implements 
AtlasEntityStore {
 
     @Override
     @GraphTransaction
-    public void deleteClassifications(final String guid, final List<String> 
classificationNames) throws AtlasBaseException {
+    public void deleteClassification(final String guid, final String 
classificationName) throws AtlasBaseException {
+        deleteClassification(guid, classificationName, null);
+    }
+
+    @Override
+    @GraphTransaction
+    public void deleteClassification(final String guid, final String 
classificationName, final String associatedEntityGuid) throws 
AtlasBaseException {
         if (StringUtils.isEmpty(guid)) {
             throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, 
"Guid(s) not specified");
         }
-        if (CollectionUtils.isEmpty(classificationNames)) {
-            throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, 
"classifications(s) not specified");
+        if (StringUtils.isEmpty(classificationName)) {
+            throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, 
"classifications not specified");
         }
 
         AtlasEntityHeader entityHeader = 
entityRetriever.toAtlasEntityHeaderWithClassifications(guid);
 
-        for (String classification : classificationNames) {
-            AtlasAuthorizationUtils.verifyAccess(new 
AtlasEntityAccessRequest(typeRegistry, 
AtlasPrivilege.ENTITY_REMOVE_CLASSIFICATION, entityHeader, new 
AtlasClassification(classification)), "remove classification: guid=", guid, ", 
classification=", classification);
+        // verify authorization only for removal of directly associated 
classification and not propagated one.
+        if (StringUtils.isEmpty(associatedEntityGuid) || 
guid.equals(associatedEntityGuid)) {
+            AtlasAuthorizationUtils.verifyAccess(new 
AtlasEntityAccessRequest(typeRegistry, 
AtlasPrivilege.ENTITY_REMOVE_CLASSIFICATION,
+                                                 entityHeader, new 
AtlasClassification(classificationName)),
+                                                 "remove classification: 
guid=", guid, ", classification=", classificationName);
         }
 
         if (LOG.isDebugEnabled()) {
-            LOG.debug("Deleting classifications={} from entity={}", 
classificationNames, guid);
+            LOG.debug("Deleting classification={} from entity={}", 
classificationName, guid);
         }
 
         GraphTransactionInterceptor.lockObjectAndReleasePostCommit(guid);
 
-        entityGraphMapper.deleteClassifications(guid, classificationNames);
+        entityGraphMapper.deleteClassification(guid, classificationName, 
associatedEntityGuid);
     }
 
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
index f57ce99..3668e47 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
@@ -67,6 +67,7 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 import static org.apache.atlas.model.TypeCategory.CLASSIFICATION;
+import static org.apache.atlas.model.instance.AtlasEntity.Status.ACTIVE;
 import static org.apache.atlas.model.instance.AtlasEntity.Status.DELETED;
 import static 
org.apache.atlas.model.instance.AtlasRelatedObjectId.KEY_RELATIONSHIP_ATTRIBUTES;
 import static 
org.apache.atlas.model.instance.EntityMutations.EntityOperation.CREATE;
@@ -83,6 +84,7 @@ import static 
org.apache.atlas.repository.graph.GraphHelper.getCollectionElement
 import static 
org.apache.atlas.repository.graph.GraphHelper.getClassificationEdge;
 import static 
org.apache.atlas.repository.graph.GraphHelper.getClassificationVertex;
 import static 
org.apache.atlas.repository.graph.GraphHelper.getMapElementsProperty;
+import static 
org.apache.atlas.repository.graph.GraphHelper.getPropagatedTraitNames;
 import static org.apache.atlas.repository.graph.GraphHelper.getStatus;
 import static org.apache.atlas.repository.graph.GraphHelper.getTraitLabel;
 import static org.apache.atlas.repository.graph.GraphHelper.getTraitNames;
@@ -271,6 +273,7 @@ public class EntityGraphMapper {
 
         AtlasGraphUtilsV2.addProperty(ret, Constants.SUPER_TYPES_PROPERTY_KEY, 
classificationType.getAllSuperTypes());
         AtlasGraphUtilsV2.setProperty(ret, 
Constants.CLASSIFICATION_ENTITY_GUID, classification.getEntityGuid());
+        AtlasGraphUtilsV2.setProperty(ret, 
Constants.CLASSIFICATION_ENTITY_STATUS, 
classification.getEntityStatus().name());
 
         return ret;
     }
@@ -1312,6 +1315,11 @@ public class EntityGraphMapper {
                     classification.setEntityGuid(guid);
                 }
 
+                // set associated entity status to classification
+                if (classification.getEntityStatus() == null) {
+                    classification.setEntityStatus(ACTIVE);
+                }
+
                 // ignore propagated classifications
 
                 if (LOG.isDebugEnabled()) {
@@ -1390,8 +1398,30 @@ public class EntityGraphMapper {
         }
     }
 
-    public void deleteClassifications(String entityGuid, List<String> 
classificationNames) throws AtlasBaseException {
-        if (CollectionUtils.isEmpty(classificationNames)) {
+    public void deleteClassification(String entityGuid, String 
classificationName, String associatedEntityGuid) throws AtlasBaseException {
+        if (StringUtils.isEmpty(associatedEntityGuid) || 
associatedEntityGuid.equals(entityGuid)) {
+            deleteClassification(entityGuid, classificationName);
+        } else {
+            deletePropagatedClassification(entityGuid, classificationName, 
associatedEntityGuid);
+        }
+    }
+
+    private void deletePropagatedClassification(String entityGuid, String 
classificationName, String associatedEntityGuid) throws AtlasBaseException {
+        if (StringUtils.isEmpty(classificationName)) {
+            throw new 
AtlasBaseException(AtlasErrorCode.INVALID_CLASSIFICATION_PARAMS, "delete", 
entityGuid);
+        }
+
+        AtlasVertex entityVertex = AtlasGraphUtilsV2.findByGuid(entityGuid);
+
+        if (entityVertex == null) {
+            throw new 
AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, entityGuid);
+        }
+
+        deleteHandler.deletePropagatedClassification(entityVertex, 
classificationName, associatedEntityGuid);
+    }
+
+    public void deleteClassification(String entityGuid, String 
classificationName) throws AtlasBaseException {
+        if (StringUtils.isEmpty(classificationName)) {
             throw new 
AtlasBaseException(AtlasErrorCode.INVALID_CLASSIFICATION_PARAMS, "delete", 
entityGuid);
         }
 
@@ -1407,57 +1437,55 @@ public class EntityGraphMapper {
             throw new 
AtlasBaseException(AtlasErrorCode.NO_CLASSIFICATIONS_FOUND_FOR_ENTITY, 
entityGuid);
         }
 
-        validateClassificationExists(traitNames, classificationNames);
+        validateClassificationExists(traitNames, classificationName);
 
         Map<AtlasVertex, List<AtlasClassification>> removedClassifications = 
new HashMap<>();
 
-        for (String classificationName : classificationNames) {
-            AtlasVertex         classificationVertex = 
getClassificationVertex(entityVertex, classificationName);
-            AtlasClassification classification       = 
entityRetriever.toAtlasClassification(classificationVertex);
+        AtlasVertex         classificationVertex = 
getClassificationVertex(entityVertex, classificationName);
+        AtlasClassification classification       = 
entityRetriever.toAtlasClassification(classificationVertex);
 
-            // remove classification from propagated entities if propagation 
is turned on
-            if (isPropagationEnabled(classificationVertex)) {
-                List<AtlasVertex> propagatedEntityVertices = 
deleteHandler.removeTagPropagation(classificationVertex);
+        // remove classification from propagated entities if propagation is 
turned on
+        if (isPropagationEnabled(classificationVertex)) {
+            List<AtlasVertex> propagatedEntityVertices = 
deleteHandler.removeTagPropagation(classificationVertex);
 
-                // add propagated entities and deleted classification details 
to removeClassifications map
-                if (CollectionUtils.isNotEmpty(propagatedEntityVertices)) {
-                    for (AtlasVertex propagatedEntityVertex : 
propagatedEntityVertices) {
-                        List<AtlasClassification> classifications = 
removedClassifications.get(propagatedEntityVertex);
+            // add propagated entities and deleted classification details to 
removeClassifications map
+            if (CollectionUtils.isNotEmpty(propagatedEntityVertices)) {
+                for (AtlasVertex propagatedEntityVertex : 
propagatedEntityVertices) {
+                    List<AtlasClassification> classifications = 
removedClassifications.get(propagatedEntityVertex);
 
-                        if (classifications == null) {
-                            classifications = new ArrayList<>();
+                    if (classifications == null) {
+                        classifications = new ArrayList<>();
 
-                            removedClassifications.put(propagatedEntityVertex, 
classifications);
-                        }
-
-                        classifications.add(classification);
+                        removedClassifications.put(propagatedEntityVertex, 
classifications);
                     }
+
+                    classifications.add(classification);
                 }
             }
+        }
 
-            // add associated entity and deleted classification details to 
removeClassifications map
-            List<AtlasClassification> classifications = 
removedClassifications.get(entityVertex);
+        // add associated entity and deleted classification details to 
removeClassifications map
+        List<AtlasClassification> classifications = 
removedClassifications.get(entityVertex);
 
-            if (classifications == null) {
-                classifications = new ArrayList<>();
+        if (classifications == null) {
+            classifications = new ArrayList<>();
 
-                removedClassifications.put(entityVertex, classifications);
-            }
+            removedClassifications.put(entityVertex, classifications);
+        }
 
-            classifications.add(classification);
+        classifications.add(classification);
 
-            // remove classifications from associated entity
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("Removing classification: [{}] from: [{}][{}] with 
edge label: [{}]", classificationName,
-                           getTypeName(entityVertex), entityGuid, 
CLASSIFICATION_LABEL);
-            }
+        // remove classifications from associated entity
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Removing classification: [{}] from: [{}][{}] with edge 
label: [{}]", classificationName,
+                    getTypeName(entityVertex), entityGuid, 
CLASSIFICATION_LABEL);
+        }
 
-            AtlasEdge edge = getClassificationEdge(entityVertex, 
classificationVertex);
+        AtlasEdge edge = getClassificationEdge(entityVertex, 
classificationVertex);
 
-            deleteHandler.deleteEdgeReference(edge, CLASSIFICATION, false, 
true, entityVertex);
+        deleteHandler.deleteEdgeReference(edge, CLASSIFICATION, false, true, 
entityVertex);
 
-            traitNames.remove(classificationName);
-        }
+        traitNames.remove(classificationName);
 
         updateTraitNamesProperty(entityVertex, traitNames);
 
@@ -1670,7 +1698,9 @@ public class EntityGraphMapper {
         List<String> traitNames = getTraitNames(instanceVertex);
 
         if (CollectionUtils.isNotEmpty(traitNames)) {
-            deleteClassifications(guid, traitNames);
+            for (String traitName : traitNames) {
+                deleteClassification(guid, traitName);
+            }
         }
     }
 
@@ -1693,6 +1723,12 @@ public class EntityGraphMapper {
         }
     }
 
+    private void validateClassificationExists(List<String> 
existingClassifications, String suppliedClassificationName) throws 
AtlasBaseException {
+        if (!existingClassifications.contains(suppliedClassificationName)) {
+            throw new 
AtlasBaseException(AtlasErrorCode.CLASSIFICATION_NOT_ASSOCIATED_WITH_ENTITY, 
suppliedClassificationName);
+        }
+    }
+
     private AtlasEdge getOrCreateRelationship(AtlasVertex end1Vertex, 
AtlasVertex end2Vertex, String relationshipName,
                                               Map<String, Object> 
relationshipAttributes) throws AtlasBaseException {
         return relationshipStore.getOrCreate(end1Vertex, end2Vertex, new 
AtlasRelationship(relationshipName, relationshipAttributes));

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphRetriever.java
----------------------------------------------------------------------
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 8f4faaf..b07ca1d 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
@@ -100,6 +100,7 @@ import static 
org.apache.atlas.repository.graph.GraphHelper.getAllClassification
 import static org.apache.atlas.repository.graph.GraphHelper.getAllTraitNames;
 import static 
org.apache.atlas.repository.graph.GraphHelper.getBlockedClassificationIds;
 import static 
org.apache.atlas.repository.graph.GraphHelper.getArrayElementsProperty;
+import static 
org.apache.atlas.repository.graph.GraphHelper.getClassificationEntityStatus;
 import static 
org.apache.atlas.repository.graph.GraphHelper.getClassificationVertices;
 import static org.apache.atlas.repository.graph.GraphHelper.getGuid;
 import static 
org.apache.atlas.repository.graph.GraphHelper.getIncomingEdgesByLabel;
@@ -276,6 +277,7 @@ public final class EntityGraphRetriever {
         AtlasClassification ret = new 
AtlasClassification(getTypeName(classificationVertex));
 
         ret.setEntityGuid(AtlasGraphUtilsV2.getProperty(classificationVertex, 
CLASSIFICATION_ENTITY_GUID, String.class));
+        
ret.setEntityStatus(getClassificationEntityStatus(classificationVertex));
         ret.setPropagate(isPropagationEnabled(classificationVertex));
 
         String strValidityPeriods = 
AtlasGraphUtilsV2.getProperty(classificationVertex, 
CLASSIFICATION_VALIDITY_PERIODS_KEY, String.class);

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2Test.java
----------------------------------------------------------------------
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 58005c4..b13a865 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
@@ -273,7 +273,7 @@ public class AtlasEntityStoreV2Test extends 
AtlasEntityTestBase {
         AtlasEntityHeader tableDefinition1 = 
response.getFirstUpdatedEntityByTypeName(TABLE_TYPE);
         AtlasEntity updatedTableDef1 = getEntityFromStore(tableDefinition1);
         validateEntity(entitiesInfo, updatedTableDef1);
-                
+
         Assert.assertTrue(partsMap.get("part0").equals(((Map<String, 
AtlasStruct>) updatedTableDef1.getAttribute("partitionsMap")).get("part0")));
 
         //update map - add a map key
@@ -905,7 +905,9 @@ public class AtlasEntityStoreV2Test extends 
AtlasEntityTestBase {
         assertTrue(classificationSet.contains(TAG_NAME));
         assertTrue(classificationSet.contains(TAG_NAME_2));
 
-        entityStore.deleteClassifications(dbEntityGuid, actualClassifications);
+        for (String actualClassificationName : actualClassifications) {
+            entityStore.deleteClassification(dbEntityGuid, 
actualClassificationName);
+        }
     }
 
     @Test(dependsOnMethods = "testCreate")
@@ -936,7 +938,7 @@ public class AtlasEntityStoreV2Test extends 
AtlasEntityTestBase {
         assertTrue(actualDBAssociatedEntityGuid.contains(dbEntityGuid));
         assertTrue(actualTblAssociatedEntityGuid.contains(tblEntityGuid));
 
-        entityStore.deleteClassifications(dbEntityGuid, 
Collections.singletonList(TAG_NAME));
-        entityStore.deleteClassifications(tblEntityGuid, 
Collections.singletonList(TAG_NAME));
+        entityStore.deleteClassification(dbEntityGuid, TAG_NAME);
+        entityStore.deleteClassification(tblEntityGuid, TAG_NAME);
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/test/java/org/apache/atlas/repository/tagpropagation/ClassificationPropagationTest.java
----------------------------------------------------------------------
diff --git 
a/repository/src/test/java/org/apache/atlas/repository/tagpropagation/ClassificationPropagationTest.java
 
b/repository/src/test/java/org/apache/atlas/repository/tagpropagation/ClassificationPropagationTest.java
index 8ceeedb..ce62ec5 100644
--- 
a/repository/src/test/java/org/apache/atlas/repository/tagpropagation/ClassificationPropagationTest.java
+++ 
b/repository/src/test/java/org/apache/atlas/repository/tagpropagation/ClassificationPropagationTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.atlas.repository.tagpropagation;
 
+import com.vividsolutions.jts.util.Assert;
 import org.apache.atlas.RequestContext;
 import org.apache.atlas.TestModules;
 import org.apache.atlas.discovery.AtlasLineageService;
@@ -25,6 +26,7 @@ import org.apache.atlas.model.instance.AtlasClassification;
 import org.apache.atlas.model.instance.AtlasEntity;
 import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
 import org.apache.atlas.model.instance.AtlasRelationship;
+import org.apache.atlas.model.instance.EntityMutationResponse;
 import org.apache.atlas.model.lineage.AtlasLineageInfo;
 import org.apache.atlas.model.lineage.AtlasLineageInfo.LineageRelation;
 import org.apache.atlas.model.typedef.AtlasClassificationDef;
@@ -57,6 +59,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static 
org.apache.atlas.AtlasErrorCode.PROPAGATED_CLASSIFICATION_REMOVAL_NOT_SUPPORTED;
 import static org.apache.atlas.graph.GraphSandboxUtil.useLocalSolr;
 import static org.apache.atlas.model.lineage.AtlasLineageInfo.LineageDirection;
 import static 
org.apache.atlas.model.typedef.AtlasRelationshipDef.PropagateTags.BOTH;
@@ -79,6 +82,10 @@ public class ClassificationPropagationTest {
     public static final String EMPLOYEES1_PROCESS      = "EMPLOYEES1_PROCESS";
     public static final String EMPLOYEES2_PROCESS      = "EMPLOYEES2_PROCESS";
     public static final String EMPLOYEES_UNION_PROCESS = 
"EMPLOYEES_UNION_PROCESS";
+    public static final String IMPORT_FILE             = 
"tag-propagation-data-1.zip";
+    public static final String EMPLOYEES_TABLE         = "EMPLOYEES_TABLE";
+    public static final String US_EMPLOYEES_TABLE      = "US_EMPLOYEES2_TABLE";
+    public static final String EMPLOYEES_PROCESS       = "EMPLOYEES_PROCESS";
 
     @Inject
     private AtlasTypeDefStore typeDefStore;
@@ -121,13 +128,21 @@ public class ClassificationPropagationTest {
     /** This test uses the lineage graph:
      *
 
-                         [Process1] ----> [Employees1]
-                        /                              \
-                       /                                \
-       [hdfs_employees]                                  [Process3] ----> [ 
EmployeesUnion ]
-                       \                                /
-                        \                             /
-                         [Process2] ----> [Employees2]
+     Lineage - 1
+     -----------
+     [Process1] ----> [Employees1]
+     /                              \
+     /                                \
+     [hdfs_employees]                                  [Process3] ----> [ 
EmployeesUnion ]
+     \                                /
+     \                             /
+     [Process2] ----> [Employees2]
+
+
+     Lineage - 2
+     -----------
+
+     [Employees] ----> [Process] ----> [ US_Employees ]
 
      */
 
@@ -443,6 +458,31 @@ public class ClassificationPropagationTest {
         assertTrue(blockedClassifications.isEmpty());
     }
 
+    @Test(dependsOnMethods = {"removeBlockedPropagatedClassifications"})
+    public void addClassification_DeleteCase() throws AtlasBaseException {
+        AtlasEntity         employees = getEntity(EMPLOYEES_TABLE);
+        AtlasClassification tag1      = new AtlasClassification("tag1");
+
+        tag1.setEntityGuid(employees.getGuid());
+        tag1.setPropagate(true);
+
+        addClassification(employees, tag1);
+
+        List<String> propagatedEntities = Arrays.asList(EMPLOYEES_PROCESS, 
US_EMPLOYEES_TABLE);
+
+        assertClassificationExistInEntities(propagatedEntities, tag1);
+
+        AtlasEntity employees_process = getEntity(EMPLOYEES_PROCESS);
+        AtlasEntity us_employees      = getEntity(US_EMPLOYEES_TABLE);
+
+        deletePropagatedClassificationExpectFail(employees_process, tag1);
+        deletePropagatedClassificationExpectFail(us_employees, tag1);
+
+        deleteEntity(EMPLOYEES_PROCESS);
+        assertClassificationExistInEntity(EMPLOYEES_PROCESS, tag1);
+        assertClassificationExistInEntity(US_EMPLOYEES_TABLE, tag1);
+    }
+
     private void assertClassificationExistInList(Set<AtlasClassification> 
classifications, AtlasClassification classification) {
         String  classificationName  = classification.getTypeName();
         String  entityGuid          = classification.getEntityGuid();
@@ -521,7 +561,7 @@ public class ClassificationPropagationTest {
 
             loadSampleClassificationDefs();
 
-            runImportWithNoParameters(importService, 
getZipSource("tag-propagation-data.zip"));
+            runImportWithNoParameters(importService, 
getZipSource(IMPORT_FILE));
 
             initializeEntitiesMap();
         } catch (AtlasBaseException | IOException e) {
@@ -560,6 +600,10 @@ public class ClassificationPropagationTest {
         entitiesMap.put(EMPLOYEES2_PROCESS, 
"c0201260-dbeb-45f4-930d-5129eab31dc9");
         entitiesMap.put(EMPLOYEES_UNION_PROCESS, 
"470a2d1e-b1fd-47de-8f2d-8dfd0a0275a7");
 
+        entitiesMap.put(EMPLOYEES_TABLE, 
"b4edad46-d00f-4e94-be39-8d2619d17e6c");
+        entitiesMap.put(US_EMPLOYEES_TABLE, 
"44acef8e-fefe-491c-87d9-e2ea6a9ad3b0");
+        entitiesMap.put(EMPLOYEES_PROCESS, 
"a1c9a281-d30b-419c-8199-7434b245d7fe");
+
         lineageInfo = 
lineageService.getAtlasLineageInfo(entitiesMap.get(HDFS_PATH_EMPLOYEES), 
LineageDirection.BOTH, 3);
     }
 
@@ -570,6 +614,13 @@ public class ClassificationPropagationTest {
         return entityWithExtInfo.getEntity();
     }
 
+    private boolean deleteEntity(String entityName) throws AtlasBaseException {
+        String                 entityGuid = entitiesMap.get(entityName);
+        EntityMutationResponse response   = entityStore.deleteById(entityGuid);
+
+        return CollectionUtils.isNotEmpty(response.getDeletedEntities());
+    }
+
     private AtlasClassification getClassification(AtlasEntity hdfs_employees, 
AtlasClassification tag2) throws AtlasBaseException {
         return entityStore.getClassification(hdfs_employees.getGuid(), 
tag2.getTypeName());
     }
@@ -595,7 +646,26 @@ public class ClassificationPropagationTest {
     }
 
     private void deleteClassifications(AtlasEntity entity, List<String> 
classificationNames) throws AtlasBaseException {
-        entityStore.deleteClassifications(entity.getGuid(), 
classificationNames);
+        for (String classificationName : classificationNames) {
+            entityStore.deleteClassification(entity.getGuid(), 
classificationName);
+        }
+    }
+
+    private void deletePropagatedClassification(AtlasEntity entity, 
AtlasClassification classification) throws AtlasBaseException {
+        deletePropagatedClassification(entity, classification.getTypeName(), 
classification.getEntityGuid());
+    }
+
+    private void deletePropagatedClassification(AtlasEntity entity, String 
classificationName, String associatedEntityGuid) throws AtlasBaseException {
+        entityStore.deleteClassification(entity.getGuid(), classificationName, 
associatedEntityGuid);
+    }
+
+    private void deletePropagatedClassificationExpectFail(AtlasEntity entity, 
AtlasClassification classification) {
+        try {
+            deletePropagatedClassification(entity, classification);
+            fail();
+        } catch (AtlasBaseException ex) {
+            Assert.equals(ex.getAtlasErrorCode(), 
PROPAGATED_CLASSIFICATION_REMOVAL_NOT_SUPPORTED);
+        }
     }
 
     private AtlasRelationship getRelationship(String fromEntityName, String 
toEntityName) throws AtlasBaseException {

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/repository/src/test/resources/tag-propagation-data-1.zip
----------------------------------------------------------------------
diff --git a/repository/src/test/resources/tag-propagation-data-1.zip 
b/repository/src/test/resources/tag-propagation-data-1.zip
new file mode 100644
index 0000000..2c60549
Binary files /dev/null and 
b/repository/src/test/resources/tag-propagation-data-1.zip differ

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java
----------------------------------------------------------------------
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java 
b/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java
index e01cba9..00b29e6 100755
--- a/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java
@@ -1083,7 +1083,7 @@ public class EntityResource {
                 perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"EntityResource.deleteTrait(" + guid + ", " + traitName + ")");
             }
 
-            entitiesStore.deleteClassifications(guid, new ArrayList<String>() 
{{ add(traitName); }});
+            entitiesStore.deleteClassification(guid, traitName);
 
             Map<String, Object> response = new HashMap<>();
             response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId());

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java 
b/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java
index fd331fa..d488b1d 100644
--- a/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java
@@ -542,7 +542,7 @@ public class EntityREST {
                 throw new 
AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, 
typeName, attributes.toString());
             }
 
-            entitiesStore.deleteClassifications(guid, 
Collections.singletonList(classificationName));
+            entitiesStore.deleteClassification(guid, classificationName);
         } finally {
             AtlasPerfTracer.log(perf);
         }
@@ -557,15 +557,17 @@ public class EntityREST {
     @Path("/guid/{guid}/classification/{classificationName}")
     @Produces(Servlets.JSON_MEDIA_TYPE)
     public void deleteClassification(@PathParam("guid") String guid,
-                                     @PathParam("classificationName") final 
String classificationName) throws AtlasBaseException {
+                                     @PathParam("classificationName") final 
String classificationName,
+                                     @QueryParam("associatedEntityGuid") final 
String associatedEntityGuid) throws AtlasBaseException {
         Servlets.validateQueryParamLength("guid", guid);
         Servlets.validateQueryParamLength("classificationName", 
classificationName);
+        Servlets.validateQueryParamLength("associatedEntityGuid", 
associatedEntityGuid);
 
         AtlasPerfTracer perf = null;
 
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
-                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"EntityREST.deleteClassification(" + guid + "," + classificationName + ")");
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"EntityREST.deleteClassification(" + guid + "," + classificationName + "," + 
associatedEntityGuid + ")");
             }
 
             if (StringUtils.isEmpty(guid)) {
@@ -574,7 +576,7 @@ public class EntityREST {
 
             ensureClassificationType(classificationName);
 
-            entitiesStore.deleteClassifications(guid, new ArrayList<String>() 
{{ add(classificationName);}} );
+            entitiesStore.deleteClassification(guid, classificationName, 
associatedEntityGuid);
         } finally {
             AtlasPerfTracer.log(perf);
         }

http://git-wip-us.apache.org/repos/asf/atlas/blob/54d623c3/webapp/src/test/java/org/apache/atlas/web/adapters/TestEntityREST.java
----------------------------------------------------------------------
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/adapters/TestEntityREST.java 
b/webapp/src/test/java/org/apache/atlas/web/adapters/TestEntityREST.java
index 78bd53c..6739364 100644
--- a/webapp/src/test/java/org/apache/atlas/web/adapters/TestEntityREST.java
+++ b/webapp/src/test/java/org/apache/atlas/web/adapters/TestEntityREST.java
@@ -20,6 +20,7 @@ package org.apache.atlas.web.adapters;
 import org.apache.atlas.RequestContext;
 import org.apache.atlas.TestModules;
 import org.apache.atlas.TestUtilsV2;
+import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.instance.AtlasClassification;
 import 
org.apache.atlas.model.instance.AtlasClassification.AtlasClassifications;
 import org.apache.atlas.model.instance.AtlasEntity;
@@ -177,7 +178,7 @@ public class TestEntityREST {
         Assert.assertNotNull(updatedClassification);
         Assert.assertEquals(updatedClassification.getAttribute("tag"), 
testClassification.getAttribute("tag"));
 
-        entityREST.deleteClassification(dbEntity.getGuid(), TestUtilsV2.PHI);
+        deleteClassification(dbEntity.getGuid(), TestUtilsV2.PHI);
     }
 
     @Test(dependsOnMethods = "testAddAndGetClassification")
@@ -193,7 +194,7 @@ public class TestEntityREST {
     @Test(dependsOnMethods = "testGetEntityWithAssociations")
     public void  testDeleteClassification() throws Exception {
 
-        entityREST.deleteClassification(dbEntity.getGuid(), 
TestUtilsV2.CLASSIFICATION);
+        deleteClassification(dbEntity.getGuid(), TestUtilsV2.CLASSIFICATION);
         final AtlasClassification.AtlasClassifications 
retrievedClassifications = entityREST.getClassifications(dbEntity.getGuid());
 
         Assert.assertNotNull(retrievedClassifications);
@@ -367,4 +368,8 @@ public class TestEntityREST {
             put(name, new String[] { value });
         }};
     }
+
+    private void deleteClassification(String guid, String classificationName) 
throws AtlasBaseException {
+        entityREST.deleteClassification(guid, classificationName, null);
+    }
 }

Reply via email to