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/10/09 05:15:00 UTC
[atlas] branch branch-2.0 updated: ATLAS-3443: Enhancements to
support 'Labels' in Atlas
This is an automated email from the ASF dual-hosted git repository.
sarath pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/branch-2.0 by this push:
new 57be959 ATLAS-3443: Enhancements to support 'Labels' in Atlas
57be959 is described below
commit 57be959dce4d3ade03b26ac8de6a4217bb957eff
Author: Sarath Subramanian <sa...@apache.org>
AuthorDate: Tue Oct 8 22:13:58 2019 -0700
ATLAS-3443: Enhancements to support 'Labels' in Atlas
(cherry picked from commit cacf361ebd5a79d2c3609da8e5a69e9428fe5af2)
---
.../org/apache/atlas/repository/Constants.java | 2 +
.../java/org/apache/atlas/AtlasConfiguration.java | 1 +
.../main/java/org/apache/atlas/AtlasErrorCode.java | 2 +
.../apache/atlas/model/instance/AtlasEntity.java | 20 +++++-
.../atlas/model/instance/AtlasEntityHeader.java | 23 ++++++-
.../repository/graph/GraphBackedSearchIndexer.java | 1 +
.../apache/atlas/repository/graph/GraphHelper.java | 20 ++++++
.../repository/store/graph/AtlasEntityStore.java | 6 ++
.../graph/v2/AtlasEntityGraphDiscoveryV2.java | 5 ++
.../store/graph/v2/AtlasEntityStoreV2.java | 46 ++++++++++++-
.../store/graph/v2/EntityGraphMapper.java | 40 ++++++++++-
.../store/graph/v2/EntityGraphRetriever.java | 1 +
.../store/graph/v2/AtlasEntityStoreV2Test.java | 77 ++++++++++++++++++++++
.../java/org/apache/atlas/web/rest/EntityREST.java | 25 +++++++
14 files changed, 262 insertions(+), 7 deletions(-)
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 357affd..0b28243 100644
--- a/common/src/main/java/org/apache/atlas/repository/Constants.java
+++ b/common/src/main/java/org/apache/atlas/repository/Constants.java
@@ -92,6 +92,7 @@ public final class Constants {
public static final String CLASSIFICATION_NAMES_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "classificationNames");
public static final String PROPAGATED_CLASSIFICATION_NAMES_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "propagatedClassificationNames");
public static final String CUSTOM_ATTRIBUTES_PROPERTY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "customAttributes");
+ public static final String LABELS_PROPERTY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "labels");
public static final String MODIFIED_BY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "modifiedBy");
@@ -184,6 +185,7 @@ public final class Constants {
public static final String CLASSIFICATION_EDGE_STATE_PROPERTY_KEY = STATE_PROPERTY_KEY;
public static final String CLASSIFICATION_LABEL = "classifiedAs";
public static final String CLASSIFICATION_NAME_DELIMITER = "|";
+ public static final String LABEL_NAME_DELIMITER = CLASSIFICATION_NAME_DELIMITER;
public static final String TERM_ASSIGNMENT_LABEL = "r:AtlasGlossarySemanticAssignment";
public static final String ATTRIBUTE_INDEX_PROPERTY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "index");
public static final String ATTRIBUTE_KEY_PROPERTY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "key");
diff --git a/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java b/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java
index f9d5fe4..979bd0a 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java
@@ -61,6 +61,7 @@ public enum AtlasConfiguration {
CUSTOM_ATTRIBUTE_KEY_MAX_LENGTH("atlas.custom.attribute.key.max.length", 50),
CUSTOM_ATTRIBUTE_VALUE_MAX_LENGTH("atlas.custom.attribute.value.max.length", 500),
+ LABEL_MAX_LENGTH("atlas.entity.label.max.length", 50),
IMPORT_TEMP_DIRECTORY("atlas.import.temp.directory", "");
private static final Configuration APPLICATION_PROPERTIES;
diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index 9812356..7a2aae2 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -158,6 +158,8 @@ public enum AtlasErrorCode {
INVALID_CUSTOM_ATTRIBUTE_KEY_LENGTH(400, "ATLAS-400-00-89", "Invalid key: {0} in custom attribute, key size should not be greater than 50"),
INVALID_CUSTOM_ATTRIBUTE_KEY_CHARACTERS(400, "ATLAS-400-00-90", "Invalid key: {0} in custom attribute, key should only contain alphanumeric characters, '_' or '-'"),
INVALID_CUSTOM_ATTRIBUTE_VALUE(400, "ATLAS-400-00-9A", "Invalid value: {0} in custom attribute, value length is greater than {1}"),
+ INVALID_LABEL_LENGTH(400, "ATLAS-400-00-9B", "Invalid label: {0}, label size should not be greater than {1}"),
+ INVALID_LABEL_CHARACTERS(400, "ATLAS-400-00-9C", "Invalid label: {0}, label should contain alphanumeric characters, '_' or '-'"),
UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to perform {1}"),
diff --git a/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntity.java b/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntity.java
index af01896..1b033b9 100644
--- a/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntity.java
+++ b/intg/src/main/java/org/apache/atlas/model/instance/AtlasEntity.java
@@ -42,6 +42,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
@@ -92,6 +93,7 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
private List<AtlasClassification> classifications;
private List<AtlasTermAssignmentHeader> meanings;
private Map<String, String> customAttributes;
+ private Set<String> labels;
@JsonIgnore
private static AtomicLong s_nextId = new AtomicLong(System.nanoTime());
@@ -215,6 +217,7 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
setRelationshipAttributes(other.getRelationshipAttributes());
setMeanings(other.getMeanings());
setCustomAttributes(other.getCustomAttributes());
+ setLabels(other.getLabels());
}
}
@@ -345,6 +348,14 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
this.customAttributes = customAttributes;
}
+ public Set<String> getLabels() {
+ return labels;
+ }
+
+ public void setLabels(Set<String> labels) {
+ this.labels = labels;
+ }
+
public List<AtlasClassification> getClassifications() { return classifications; }
public void setClassifications(List<AtlasClassification> classifications) { this.classifications = classifications; }
@@ -393,6 +404,7 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
setClassifications(null);
setMeanings(null);
setCustomAttributes(null);
+ setLabels(null);
}
private static String nextInternalId() {
@@ -430,6 +442,9 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
sb.append(", customAttributes=[");
dumpObjects(customAttributes, sb);
sb.append("]");
+ sb.append(", labels=[");
+ dumpObjects(labels, sb);
+ sb.append("]");
sb.append('}');
return sb;
@@ -455,13 +470,14 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
Objects.equals(version, that.version) &&
Objects.equals(relationshipAttributes, that.relationshipAttributes) &&
Objects.equals(customAttributes, that.customAttributes) &&
+ Objects.equals(labels, that.labels) &&
Objects.equals(classifications, that.classifications);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), guid, homeId, isProxy, isIncomplete, provenanceType, status,
- createdBy, updatedBy, createTime, updateTime, version, relationshipAttributes, classifications, customAttributes);
+ return Objects.hash(super.hashCode(), guid, homeId, isProxy, isIncomplete, provenanceType, status, createdBy,
+ updatedBy, createTime, updateTime, version, relationshipAttributes, classifications, customAttributes, labels);
}
@Override
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 3aef6cd..7d2476a 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
@@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.PUBLIC_ONLY;
@@ -61,6 +62,7 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
private List<String> meaningNames = null;
private List<AtlasTermAssignmentHeader> meanings = null;
private Boolean isIncomplete = Boolean.FALSE;
+ private Set<String> labels = null;
public AtlasEntityHeader() {
this(null, null);
@@ -79,6 +81,7 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
setClassificationNames(null);
setClassifications(null);
+ setLabels(null);
}
@@ -87,6 +90,7 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
setGuid(guid);
setClassificationNames(null);
setClassifications(null);
+ setLabels(null);
}
@@ -100,6 +104,7 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
setClassificationNames(other.getClassificationNames());
setClassifications(other.getClassifications());
setIsIncomplete(other.getIsIncomplete());
+ setLabels(other.getLabels());
}
}
@@ -117,6 +122,10 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
this.classificationNames.add(classification.getTypeName());
}
}
+
+ if (CollectionUtils.isNotEmpty(entity.getLabels())) {
+ setLabels(entity.getLabels());
+ }
}
public String getGuid() {
@@ -159,6 +168,14 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
this.classifications = classifications;
}
+ public Set<String> getLabels() {
+ return labels;
+ }
+
+ public void setLabels(Set<String> labels) {
+ this.labels = labels;
+ }
+
public Boolean getIsIncomplete() {
return isIncomplete;
}
@@ -183,6 +200,9 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
sb.append("classifications=[");
AtlasBaseTypeDef.dumpObjects(classifications, sb);
sb.append("], ");
+ sb.append("labels=[");
+ dumpObjects(labels, sb);
+ sb.append("], ");
sb.append("isIncomplete=").append(isIncomplete);
super.toString(sb);
sb.append('}');
@@ -202,13 +222,14 @@ public class AtlasEntityHeader extends AtlasStruct implements Serializable {
Objects.equals(classificationNames, that.classificationNames) &&
Objects.equals(meaningNames, that.classificationNames) &&
Objects.equals(classifications, that.classifications) &&
+ Objects.equals(labels, that.labels) &&
Objects.equals(isIncomplete, that.isIncomplete) &&
Objects.equals(meanings, that.meanings);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), guid, status, displayText, classificationNames, classifications, meaningNames, meanings, isIncomplete);
+ return Objects.hash(super.hashCode(), guid, status, displayText, classificationNames, classifications, meaningNames, meanings, isIncomplete, labels);
}
@Override
diff --git a/repository/src/main/java/org/apache/atlas/repository/graph/GraphBackedSearchIndexer.java b/repository/src/main/java/org/apache/atlas/repository/graph/GraphBackedSearchIndexer.java
index 5d53cfd..861e344 100755
--- a/repository/src/main/java/org/apache/atlas/repository/graph/GraphBackedSearchIndexer.java
+++ b/repository/src/main/java/org/apache/atlas/repository/graph/GraphBackedSearchIndexer.java
@@ -325,6 +325,7 @@ public class GraphBackedSearchIndexer implements SearchIndexer, ActiveStateChang
createCommonVertexIndex(management, PROPAGATED_TRAIT_NAMES_PROPERTY_KEY, UniqueKind.NONE, String.class, LIST, true, true);
createCommonVertexIndex(management, IS_INCOMPLETE_PROPERTY_KEY, UniqueKind.NONE, Integer.class, SINGLE, true, true);
createCommonVertexIndex(management, CUSTOM_ATTRIBUTES_PROPERTY_KEY, UniqueKind.NONE, String.class, SINGLE, true, false);
+ createCommonVertexIndex(management, LABELS_PROPERTY_KEY, UniqueKind.NONE, String.class, SINGLE, true, false);
createCommonVertexIndex(management, PATCH_ID_PROPERTY_KEY, UniqueKind.GLOBAL_UNIQUE, String.class, SINGLE, true, false);
createCommonVertexIndex(management, PATCH_DESCRIPTION_PROPERTY_KEY, UniqueKind.NONE, String.class, SINGLE, true, false);
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 3dad388..173165a 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
@@ -1076,6 +1076,10 @@ public final class GraphHelper {
return ret;
}
+ public static Set<String> getLabels(AtlasElement element) {
+ return parseLabelsString(element.getProperty(LABELS_PROPERTY_KEY, String.class));
+ }
+
public static Integer getProvenanceType(AtlasElement element) {
return element.getProperty(Constants.PROVENANCE_TYPE_KEY, Integer.class);
}
@@ -1845,4 +1849,20 @@ public final class GraphHelper {
}
return ret;
}
+
+ private static Set<String> parseLabelsString(String labels) {
+ Set<String> ret = null;
+
+ if (StringUtils.isNotEmpty(labels)) {
+ ret = new HashSet<>();
+
+ for (String label : labels.split("\\" + LABEL_NAME_DELIMITER)) {
+ if (StringUtils.isNotEmpty(label)) {
+ ret.add(label);
+ }
+ }
+ }
+
+ return ret;
+ }
}
\ No newline at end of file
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 01b8d19..a986520 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
@@ -32,6 +32,7 @@ import org.apache.atlas.type.AtlasEntityType;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Persistence/Retrieval API for AtlasEntity
@@ -231,4 +232,9 @@ public interface AtlasEntityStore {
AtlasClassification getClassification(String guid, String classificationName) throws AtlasBaseException;
String setClassifications(AtlasEntityHeaders entityHeaders);
+
+ /**
+ * Set Labels
+ */
+ void setLabels(String guid, Set<String> labels) throws AtlasBaseException;
}
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 5547c39..d3d9367 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
@@ -51,6 +51,7 @@ import java.util.Map;
import java.util.Set;
import static org.apache.atlas.repository.store.graph.v2.EntityGraphMapper.validateCustomAttributes;
+import static org.apache.atlas.repository.store.graph.v2.EntityGraphMapper.validateLabels;
public class AtlasEntityGraphDiscoveryV2 implements EntityGraphDiscovery {
private static final Logger LOG = LoggerFactory.getLogger(AtlasEntityGraphDiscoveryV2.class);
@@ -97,6 +98,8 @@ public class AtlasEntityGraphDiscoveryV2 implements EntityGraphDiscovery {
validateCustomAttributes(entity);
+ validateLabels(entity.getLabels());
+
type.validateValue(entity, entity.getTypeName(), messages);
if (!messages.isEmpty()) {
@@ -122,6 +125,8 @@ public class AtlasEntityGraphDiscoveryV2 implements EntityGraphDiscovery {
validateCustomAttributes(entity);
+ validateLabels(entity.getLabels());
+
type.validateValueForUpdate(entity, entity.getTypeName(), messages);
if (!messages.isEmpty()) {
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 06335c0..5b3c27d 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
@@ -22,15 +22,22 @@ import org.apache.atlas.AtlasErrorCode;
import org.apache.atlas.GraphTransactionInterceptor;
import org.apache.atlas.RequestContext;
import org.apache.atlas.annotation.GraphTransaction;
+import org.apache.atlas.authorize.AtlasAuthorizationUtils;
import org.apache.atlas.authorize.AtlasEntityAccessRequest;
import org.apache.atlas.authorize.AtlasPrivilege;
-import org.apache.atlas.authorize.AtlasAuthorizationUtils;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.TypeCategory;
-import org.apache.atlas.model.instance.*;
+import org.apache.atlas.model.instance.AtlasCheckStateRequest;
+import org.apache.atlas.model.instance.AtlasCheckStateResult;
+import org.apache.atlas.model.instance.AtlasClassification;
+import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo;
import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
import org.apache.atlas.model.instance.AtlasEntity.Status;
+import org.apache.atlas.model.instance.AtlasEntityHeader;
+import org.apache.atlas.model.instance.AtlasEntityHeaders;
+import org.apache.atlas.model.instance.AtlasObjectId;
+import org.apache.atlas.model.instance.EntityMutationResponse;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.repository.store.graph.AtlasEntityStore;
import org.apache.atlas.repository.store.graph.EntityGraphDiscovery;
@@ -53,7 +60,13 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import static java.lang.Boolean.FALSE;
import static org.apache.atlas.model.instance.EntityMutations.EntityOperation.DELETE;
@@ -61,6 +74,7 @@ import static org.apache.atlas.model.instance.EntityMutations.EntityOperation.UP
import static org.apache.atlas.repository.Constants.IS_INCOMPLETE_PROPERTY_KEY;
import static org.apache.atlas.repository.graph.GraphHelper.getCustomAttributes;
import static org.apache.atlas.repository.graph.GraphHelper.isEntityIncomplete;
+import static org.apache.atlas.repository.store.graph.v2.EntityGraphMapper.validateLabels;
@Component
@@ -727,6 +741,32 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
return associator.setClassifications(entityHeaders.getGuidHeaderMap());
}
+ @Override
+ @GraphTransaction
+ public void setLabels(String guid, Set<String> labels) throws AtlasBaseException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> setLabels()");
+ }
+
+ if (StringUtils.isEmpty(guid)) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "guid is null/empty");
+ }
+
+ AtlasVertex entityVertex = AtlasGraphUtilsV2.findByGuid(guid);
+
+ if (entityVertex == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
+ }
+
+ validateLabels(labels);
+
+ entityGraphMapper.setLabels(entityVertex, labels);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== setLabels()");
+ }
+ }
+
private EntityMutationResponse createOrUpdate(EntityStream entityStream, boolean isPartialUpdate, boolean replaceClassifications) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> createOrUpdate()");
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 4130014..48466c1 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
@@ -76,6 +76,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import static org.apache.atlas.AtlasConfiguration.LABEL_MAX_LENGTH;
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;
@@ -116,6 +117,7 @@ public class EntityGraphMapper {
private static final boolean WARN_ON_NO_RELATIONSHIP = AtlasConfiguration.RELATIONSHIP_WARN_NO_RELATIONSHIPS.getBoolean();
private static final String CLASSIFICATION_NAME_DELIMITER = "|";
private static final Pattern CUSTOM_ATTRIBUTE_KEY_REGEX = Pattern.compile("^[a-zA-Z0-9_-]*$");
+ private static final Pattern LABEL_REGEX = Pattern.compile("^[a-zA-Z0-9_-]*$");
private static final int CUSTOM_ATTRIBUTE_KEY_MAX_LENGTH = AtlasConfiguration.CUSTOM_ATTRIBUTE_KEY_MAX_LENGTH.getInt();
private static final int CUSTOM_ATTRIBUTE_VALUE_MAX_LENGTH = AtlasConfiguration.CUSTOM_ATTRIBUTE_VALUE_MAX_LENGTH.getInt();
@@ -201,6 +203,8 @@ public class EntityGraphMapper {
setCustomAttributes(ret, entity);
+ setLabels(ret, entity.getLabels());
+
GraphTransactionInterceptor.addToVertexCache(guid, ret);
return ret;
@@ -319,7 +323,7 @@ public class EntityGraphMapper {
return resp;
}
- public void setCustomAttributes(AtlasVertex vertex, AtlasEntity entity) throws AtlasBaseException {
+ public void setCustomAttributes(AtlasVertex vertex, AtlasEntity entity) {
String customAttributesString = getCustomAttributesString(entity);
if (customAttributesString != null) {
@@ -327,6 +331,24 @@ public class EntityGraphMapper {
}
}
+ public void setLabels(AtlasVertex vertex, Set<String> labels) {
+ if (CollectionUtils.isNotEmpty(labels)) {
+ AtlasGraphUtilsV2.setEncodedProperty(vertex, LABELS_PROPERTY_KEY, getLabelString(labels));
+ } else {
+ vertex.removeProperty(LABELS_PROPERTY_KEY);
+ }
+ }
+
+ private String getLabelString(Set<String> labels) {
+ String ret = null;
+
+ if (!labels.isEmpty()) {
+ ret = LABEL_NAME_DELIMITER + String.join(LABEL_NAME_DELIMITER, labels) + LABEL_NAME_DELIMITER;
+ }
+
+ return ret;
+ }
+
private AtlasVertex createStructVertex(AtlasStruct struct) {
return createStructVertex(struct.getTypeName());
}
@@ -2206,4 +2228,20 @@ public class EntityGraphMapper {
}
}
}
+
+ public static void validateLabels(Set<String> labels) throws AtlasBaseException {
+ if (CollectionUtils.isNotEmpty(labels)) {
+ for (String label : labels) {
+ if (label.length() > LABEL_MAX_LENGTH.getInt()) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_LABEL_LENGTH, label, String.valueOf(LABEL_MAX_LENGTH.getInt()));
+ }
+
+ Matcher matcher = LABEL_REGEX.matcher(label);
+
+ if (!matcher.matches()) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_LABEL_CHARACTERS, label);
+ }
+ }
+ }
+ }
}
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 9d9f6ef..7853dc1 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
@@ -583,6 +583,7 @@ public class EntityGraphRetriever {
entity.setProvenanceType(GraphHelper.getProvenanceType(entityVertex));
entity.setCustomAttributes(getCustomAttributes(entityVertex));
+ entity.setLabels(getLabels(entityVertex));
return entity;
}
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 b1506f8..d95c127 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
@@ -1108,4 +1108,81 @@ public class AtlasEntityStoreV2Test extends AtlasEntityTestBase {
assertEquals(ex.getAtlasErrorCode(), INVALID_CUSTOM_ATTRIBUTE_VALUE);
}
}
+
+ @Test(dependsOnMethods = "testCreate")
+ public void addLabelsToEntity() throws AtlasBaseException {
+ Set<String> labels = new HashSet<>();
+ labels.add("label_1");
+ labels.add("label_2");
+ labels.add("label_3");
+ labels.add("label_4");
+ labels.add("label_5");
+
+ entityStore.setLabels(tblEntityGuid, labels);
+
+ AtlasEntity tblEntity = getEntityFromStore(tblEntityGuid);
+
+ assertEquals(labels, tblEntity.getLabels());
+ }
+
+ @Test (dependsOnMethods = "addLabelsToEntity")
+ public void updateLabelsToEntity() throws AtlasBaseException {
+ Set<String> labels = new HashSet<>();
+ labels.add("label_1_update");
+ labels.add("label_2_update");
+ labels.add("label_3_update");
+
+ entityStore.setLabels(tblEntityGuid, labels);
+
+ AtlasEntity tblEntity = getEntityFromStore(tblEntityGuid);
+
+ assertEquals(labels, tblEntity.getLabels());
+ }
+
+ @Test (dependsOnMethods = "updateLabelsToEntity")
+ public void clearLabelsToEntity() throws AtlasBaseException {
+ HashSet<String> emptyLabels = new HashSet<>();
+
+ entityStore.setLabels(tblEntityGuid, emptyLabels);
+
+ AtlasEntity tblEntity = getEntityFromStore(tblEntityGuid);
+
+ Assert.assertNull(tblEntity.getLabels());
+ }
+
+ @Test (dependsOnMethods = "clearLabelsToEntity")
+ public void nullLabelsToEntity() throws AtlasBaseException {
+ entityStore.setLabels(tblEntityGuid, null);
+
+ AtlasEntity tblEntity = getEntityFromStore(tblEntityGuid);
+
+ Assert.assertNull(tblEntity.getLabels());
+ }
+
+ @Test (dependsOnMethods = "nullLabelsToEntity")
+ public void invalidLabelLengthToEntity() throws AtlasBaseException {
+ Set<String> labels = new HashSet<>();
+ labels.add(randomAlphanumeric(50));
+ labels.add(randomAlphanumeric(51));
+
+ try {
+ entityStore.setLabels(tblEntityGuid, labels);
+ } catch (AtlasBaseException ex) {
+ assertEquals(ex.getAtlasErrorCode(), INVALID_LABEL_LENGTH);
+ }
+ }
+
+ @Test (dependsOnMethods = "invalidLabelLengthToEntity")
+ public void invalidLabelCharactersToEntity() throws AtlasBaseException {
+ Set<String> labels = new HashSet<>();
+ labels.add("label-1_100_45");
+ labels.add("LABEL-1_200-55");
+ labels.add("LaBeL-1-)(*U&%^%#$@!~");
+
+ try {
+ entityStore.setLabels(tblEntityGuid, labels);
+ } catch (AtlasBaseException ex) {
+ assertEquals(ex.getAtlasErrorCode(), INVALID_LABEL_CHARACTERS);
+ }
+ }
}
\ No newline at end of file
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 e5b9563..e5f7d33 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
@@ -67,6 +67,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
@@ -813,6 +814,30 @@ public class EntityREST {
}
}
+ /**
+ * Set labels to a given entity
+ * @param guid - Unique entity identifier
+ * @param labels - set of labels to be set to the entity
+ * @throws AtlasBaseException
+ */
+ @POST
+ @Path("/guid/{guid}/labels")
+ @Produces(Servlets.JSON_MEDIA_TYPE)
+ @Consumes(Servlets.JSON_MEDIA_TYPE)
+ public void setLabels(@PathParam("guid") final String guid, Set<String> labels) throws AtlasBaseException {
+ AtlasPerfTracer perf = null;
+
+ try {
+ if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+ perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.setLabels()");
+ }
+
+ entitiesStore.setLabels(guid, labels);
+ } finally {
+ AtlasPerfTracer.log(perf);
+ }
+ }
+
private AtlasEntityType ensureEntityType(String typeName) throws AtlasBaseException {
AtlasEntityType ret = typeRegistry.getEntityTypeByName(typeName);