diff --git a/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntityHeader.java b/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntityHeader.java index 13820f5255..c628451c2c 100644 --- a/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntityHeader.java +++ b/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntityHeader.java @@ -61,6 +61,11 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable { private String displayText = null; private List classificationNames = null; private List classifications = null; + private List appendClassifications = null; + + + private List updateClassifications = null; + private List removeClassifications = null; private List meaningNames = null; private List meanings = null; private Boolean isIncomplete = Boolean.FALSE; @@ -101,6 +106,29 @@ public AtlasEntityHeader(String typeName, Map attributes) { setLabels(null); } + public List getAppendClassifications() { + return appendClassifications; + } + + public void setAppendClassifications(List appendClassifications) { + this.appendClassifications = appendClassifications; + } + + public List getUpdateClassifications() { + return updateClassifications; + } + + public void setUpdateClassifications(List updateClassifications) { + this.updateClassifications = updateClassifications; + } + + public List getRemoveClassifications() { + return removeClassifications; + } + + public void setRemoveClassifications(List removeClassifications) { + this.removeClassifications = removeClassifications; + } public AtlasEntityHeader(String typeName, String guid, Map attributes) { super(typeName, attributes); diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/ClassificationAssociator.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/ClassificationAssociator.java index 6cd9ac4e4b..732fabb120 100644 --- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/ClassificationAssociator.java +++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/ClassificationAssociator.java @@ -53,6 +53,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.apache.atlas.AtlasConfiguration.ENTITY_CHANGE_NOTIFY_IGNORE_RELATIONSHIP_ATTRIBUTES; @@ -154,7 +155,7 @@ public Updater(AtlasTypeRegistry typeRegistry, AtlasEntityStore entitiesStore, this(AtlasGraphProvider.getGraphInstance(), typeRegistry, entitiesStore, entityGraphMapper, entityChangeNotifier, instanceConverter); } - public void setClassifications(Map map) throws AtlasBaseException { + public void setClassifications(Map map, boolean overrideClassifications) throws AtlasBaseException { RequestContext.get().setDelayTagNotifications(true); for (String guid : map.keySet()) { @@ -178,8 +179,12 @@ public void setClassifications(Map map) throws AtlasB if (entityToBeChanged == null) { throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid); } - - Map> operationListMap = computeChanges(incomingEntityHeader, entityToBeChanged); + Map> operationListMap = new HashMap<>(); + if(overrideClassifications) { + operationListMap = computeChanges(incomingEntityHeader, entityToBeChanged); + } else { + operationListMap = validateAndTransfer(incomingEntityHeader, entityToBeChanged); + } try { commitChanges(guid, typeName, operationListMap); } catch (AtlasBaseException e) { @@ -236,6 +241,44 @@ public void setClassifications(Map map) throws AtlasB RequestContext.get().setDelayTagNotifications(false); } + private Map> validateAndTransfer(AtlasEntityHeader incomingEntityHeader, AtlasEntityHeader entityToBeChanged) throws AtlasBaseException { + Map> operationListMap = new HashMap<>(); + + Set requiredClassificationKeys = Stream.concat( + incomingEntityHeader.getRemoveClassifications().stream(), + incomingEntityHeader.getUpdateClassifications().stream() + ).map(this::generateClassificationComparisonKey) + .collect(Collectors.toSet()); + + Set preExistingClassificationKeys = entityToBeChanged.getClassifications() + .stream() + .map(this::generateClassificationComparisonKey) + .collect(Collectors.toSet()); + + Set diff = requiredClassificationKeys.stream() + .filter(key -> !preExistingClassificationKeys.contains(key)) + .collect(Collectors.toSet()); + + if (!diff.isEmpty()) { + String firstTypeName = diff.iterator().next().split("\\|")[1]; + throw new AtlasBaseException(AtlasErrorCode.CLASSIFICATION_NOT_ASSOCIATED_WITH_ENTITY, firstTypeName); + } + + List filteredClassifications = incomingEntityHeader.getAppendClassifications() + .stream() + .filter(appendClassification -> !preExistingClassificationKeys.contains(generateClassificationComparisonKey(appendClassification))) + .collect(Collectors.toList()); + + bucket(PROCESS_DELETE, operationListMap, incomingEntityHeader.getRemoveClassifications()); + bucket(PROCESS_UPDATE, operationListMap, incomingEntityHeader.getUpdateClassifications()); + bucket(PROCESS_ADD, operationListMap, filteredClassifications); + return operationListMap; + } + + private String generateClassificationComparisonKey(AtlasClassification classification) { + return classification.getEntityGuid() + "|" + classification.getTypeName(); + } + private void commitChanges(String entityGuid, String typeName, Map> operationListMap) throws AtlasBaseException { if (MapUtils.isEmpty(operationListMap)) { return; 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 5333d63e7b..fc214fea3c 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 @@ -1265,7 +1265,7 @@ public AtlasEntityHeaders getEntityHeaders(@QueryParam("tagUpdateStartTime") lon @Produces(Servlets.JSON_MEDIA_TYPE) @Consumes(Servlets.JSON_MEDIA_TYPE) @Timed - public void setClassifications(AtlasEntityHeaders entityHeaders) throws AtlasBaseException { + public void setClassifications(AtlasEntityHeaders entityHeaders, @QueryParam("overrideClassifications") @DefaultValue("true") boolean overrideClassifications) throws AtlasBaseException { AtlasPerfTracer perf = null; try { @@ -1274,7 +1274,7 @@ public void setClassifications(AtlasEntityHeaders entityHeaders) throws AtlasBas } ClassificationAssociator.Updater associator = new ClassificationAssociator.Updater(typeRegistry, entitiesStore, entityGraphMapper, entityChangeNotifier, instanceConverter); - associator.setClassifications(entityHeaders.getGuidHeaderMap()); + associator.setClassifications(entityHeaders.getGuidHeaderMap(), overrideClassifications); } finally { AtlasPerfTracer.log(perf); }