Commit d3a08b6e by Mandar Ambawane Committed by Madhan Neethiraj

ATLAS-3534: enhancements to support add/update/delete namespace-attributes on entities

parent 984b359c
......@@ -88,14 +88,13 @@ public final class Constants {
public static final String VERSION_PROPERTY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "version");
public static final String STATE_PROPERTY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "state");
public static final String CREATED_BY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "createdBy");
public static final String MODIFIED_BY_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "modifiedBy");
public static final String CLASSIFICATION_TEXT_KEY = encodePropertyKey(INTERNAL_PROPERTY_KEY_PREFIX + "classificationsText");
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");
/**
* Patch vertices property keys.
*/
......
......@@ -147,25 +147,28 @@ public enum AtlasErrorCode {
ATTRIBUTE_TYPE_INVALID(400, "ATLAS-400-00-081", "{0}.{1}: invalid attribute type. Attribute cannot be of type classification"),
MISSING_CATEGORY_DISPLAY_NAME(400, "ATLAS-400-00-082", "Category name is empty/null"),
INVALID_DISPLAY_NAME(400, "ATLAS-400-00-083", "name cannot contain following special chars ('@', '.')"),
TERM_HAS_ENTITY_ASSOCIATION(400, "ATLAS-400-00-086", "Term (guid={0}) can't be deleted as it has been assigned to {1} entities."),
INVALID_TIMEBOUNDRY_TIMEZONE(400, "ATLAS-400-00-87A", "Invalid timezone {0}"),
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"),
IMPORT_ATTEMPTING_EMPTY_ZIP(400, "ATLAS-400-00-87F", "Attempting to import empty ZIP file."),
PATCH_MISSING_RELATIONSHIP_LABEL(400, "ATLAS-400-00-88", "{0} - must include relationship label for type {1}"),
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 '-'"),
INVALID_PROPAGATION_TYPE(400, "ATLAS-400-00-9D", "Invalid propagation {0} for relationship-type={1}. Default value is {2}"),
DUPLICATE_NAMESPACE_ATTRIBUTE(400, "ATLAS-400-00-094", "Duplicate Namespace Attributes: {0} not allowed within the same namespace: {1}"),
APPLICABLE_ENTITY_TYPES_DELETION_NOT_SUPPORTED(400, "ATLAS-400-00-095", "Cannot remove applicableEntityTypes in Attribute Def: {1}, defined in namespace: {2}"),
NAMESPACE_DEF_MANDATORY_ATTRIBUTE_NOT_ALLOWED(400, "ATLAS-400-00-096", "{0}.{1} : namespaces can not have mandatory attribute"),
NAMESPACE_DEF_UNIQUE_ATTRIBUTE_NOT_ALLOWED(400, "ATLAS-400-00-097", "{0}.{1} : namespaces can not have unique attribute"),
NAMESPACE_DEF_ATTRIBUTE_TYPE_INVALID(400, "ATLAS-400-00-098", "{0}.{1}: invalid attribute type. Namespace attribute cannot be of type entity/classification/struct/namespace"),
TERM_HAS_ENTITY_ASSOCIATION(400, "ATLAS-400-00-084", "Term (guid={0}) can't be deleted as it has been assigned to {1} entities."),
INVALID_TIMEBOUNDRY_TIMEZONE(400, "ATLAS-400-00-085", "Invalid timezone {0}"),
INVALID_TIMEBOUNDRY_START_TIME(400, "ATLAS-400-00-086", "Invalid startTime {0}"),
INVALID_TIMEBOUNDRY_END_TIME(400, "ATLAS-400-00-087", "Invalid endTime {0}"),
INVALID_TIMEBOUNDRY_DATERANGE(400, "ATLAS-400-00-088", "Invalid dateRange: startTime {0} must be before endTime {1}"),
PROPAGATED_CLASSIFICATION_REMOVAL_NOT_SUPPORTED(400, "ATLAS-400-00-089", "Removal of classification {0}, which is propagated from entity {1}, is not supported"),
IMPORT_ATTEMPTING_EMPTY_ZIP(400, "ATLAS-400-00-08A", "Attempting to import empty ZIP file."),
PATCH_MISSING_RELATIONSHIP_LABEL(400, "ATLAS-400-00-08B", "{0} - must include relationship label for type {1}"),
INVALID_CUSTOM_ATTRIBUTE_KEY_LENGTH(400, "ATLAS-400-00-08C", "Invalid key: {0} in custom attribute, key size should not be greater than 50"),
INVALID_CUSTOM_ATTRIBUTE_KEY_CHARACTERS(400, "ATLAS-400-00-08D", "Invalid key: {0} in custom attribute, key should only contain alphanumeric characters, '_' or '-'"),
INVALID_CUSTOM_ATTRIBUTE_VALUE(400, "ATLAS-400-00-08E", "Invalid value: {0} in custom attribute, value length is greater than {1}"),
INVALID_LABEL_LENGTH(400, "ATLAS-400-00-08F", "Invalid label: {0}, label size should not be greater than {1}"),
INVALID_LABEL_CHARACTERS(400, "ATLAS-400-00-090", "Invalid label: {0}, label should contain alphanumeric characters, '_' or '-'"),
INVALID_PROPAGATION_TYPE(400, "ATLAS-400-00-091", "Invalid propagation {0} for relationship-type={1}. Default value is {2}"),
DUPLICATE_NAMESPACE_ATTRIBUTE(400, "ATLAS-400-00-092", "Duplicate Namespace Attributes: {0} not allowed within the same namespace: {1}"),
APPLICABLE_ENTITY_TYPES_DELETION_NOT_SUPPORTED(400, "ATLAS-400-00-093", "Cannot remove applicableEntityTypes in Attribute Def: {0}, defined in namespace: {1}"),
NAMESPACE_DEF_MANDATORY_ATTRIBUTE_NOT_ALLOWED(400, "ATLAS-400-00-094", "{0}.{1} : namespaces can not have mandatory attribute"),
NAMESPACE_DEF_UNIQUE_ATTRIBUTE_NOT_ALLOWED(400, "ATLAS-400-00-095", "{0}.{1} : namespaces can not have unique attribute"),
NAMESPACE_DEF_ATTRIBUTE_TYPE_INVALID(400, "ATLAS-400-00-096", "{0}.{1}: invalid attribute type. Namespace attribute cannot be of type entity/classification/struct/namespace"),
INVALID_NAMESPACE_NAME_FOR_ENTITY_TYPE(400, "ATLAS-400-00-097", "Invalid Namespace: {0} specified for entity, applicable namespaces: {1}"),
NAMESPACE_ATTRIBUTE_DOES_NOT_EXIST(400, "ATLAS-400-00-098", "Namespace attribute does not exist in entity: {0}"),
NAMESPACE_ATTRIBUTE_ALREADY_EXISTS(400, "ATLAS-400-00-099", "Namespace attribute already exists in entity: {0}"),
UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to perform {1}"),
......
......@@ -93,6 +93,7 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
private List<AtlasClassification> classifications;
private List<AtlasTermAssignmentHeader> meanings;
private Map<String, String> customAttributes;
private Map<String, Map<String, Object>> namespaceAttributes;
private Set<String> labels;
@JsonIgnore
......@@ -217,6 +218,7 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
setRelationshipAttributes(other.getRelationshipAttributes());
setMeanings(other.getMeanings());
setCustomAttributes(other.getCustomAttributes());
setNamespaceAttributes(other.getNamespaceAttributes());
setLabels(other.getLabels());
}
}
......@@ -348,6 +350,41 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
this.customAttributes = customAttributes;
}
public Map<String, Map<String, Object>> getNamespaceAttributes() {
return namespaceAttributes;
}
public void setNamespaceAttributes(Map<String, Map<String, Object>> namespaceAttributes) {
this.namespaceAttributes = namespaceAttributes;
}
public void setNamespaceAttribute(String nsName, String nsAttrName, Object nsValue) {
Map<String, Map<String, Object>> namespaceAttributes = this.namespaceAttributes;
if (namespaceAttributes == null) {
namespaceAttributes = new HashMap<>();
this.namespaceAttributes = namespaceAttributes;
}
Map<String, Object> namespaceAttributeMap = namespaceAttributes.get(nsName);
if (namespaceAttributeMap == null) {
namespaceAttributeMap = new HashMap<>();
namespaceAttributes.put(nsName, namespaceAttributeMap);
}
namespaceAttributeMap.put(nsAttrName, nsValue);
}
public Object getNamespaceAttribute(String nsName, String nsAttrName) {
Map<String, Map<String, Object>> namespaceAttributes = this.namespaceAttributes;
Map<String, Object> namespaceAttributeMap = namespaceAttributes == null ? null : namespaceAttributes.get(nsName);
return namespaceAttributeMap == null ? null : namespaceAttributeMap.get(nsAttrName);
}
public Set<String> getLabels() {
return labels;
}
......@@ -404,6 +441,7 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
setClassifications(null);
setMeanings(null);
setCustomAttributes(null);
setNamespaceAttributes(null);
setLabels(null);
}
......@@ -442,6 +480,9 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
sb.append(", customAttributes=[");
dumpObjects(customAttributes, sb);
sb.append("]");
sb.append(", namespaceAttributes=[");
dumpObjects(namespaceAttributes, sb);
sb.append("]");
sb.append(", labels=[");
dumpObjects(labels, sb);
sb.append("]");
......@@ -470,14 +511,15 @@ public class AtlasEntity extends AtlasStruct implements Serializable {
Objects.equals(version, that.version) &&
Objects.equals(relationshipAttributes, that.relationshipAttributes) &&
Objects.equals(customAttributes, that.customAttributes) &&
Objects.equals(namespaceAttributes, that.namespaceAttributes) &&
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, labels);
return Objects.hash(super.hashCode(), guid, homeId, isProxy, isIncomplete, provenanceType, status, createdBy, updatedBy,
createTime, updateTime, version, relationshipAttributes, classifications, customAttributes, namespaceAttributes, labels);
}
@Override
......
......@@ -151,14 +151,15 @@ public class AtlasNamespaceType extends AtlasStructType {
public AtlasNamespaceAttribute(AtlasAttribute attribute, Set<AtlasEntityType> applicableEntityTypes) {
super(attribute);
this.maxStringLength = 0;
this.validPattern = null;
this.applicableEntityTypes = applicableEntityTypes;
}
public AtlasNamespaceAttribute(AtlasAttribute attribute, Set<AtlasEntityType> applicableEntityTypes,
int maxStringLength, String validPattern) {
public AtlasNamespaceAttribute(AtlasAttribute attribute, Set<AtlasEntityType> applicableEntityTypes, int maxStringLength, String validPattern) {
super(attribute);
this.maxStringLength = maxStringLength;
this.validPattern = validPattern;
this.applicableEntityTypes = applicableEntityTypes;
......
......@@ -201,6 +201,7 @@ public class AtlasTypeRegistry {
return registryData.namespaceDefs.getAll();
}
public AtlasNamespaceType getNamespaceTypeByName(String name) { return registryData.namespaceDefs.getTypeByName(name); }
public Collection<AtlasRelationshipDef> getAllRelationshipDefs() { return registryData.relationshipDefs.getAll(); }
......
......@@ -248,6 +248,23 @@ public interface AtlasEntityStore {
void setLabels(String guid, Set<String> labels) throws AtlasBaseException;
/**
*
* @param guid
* @param namespaceAttributes
* @param isOverwrite
* @throws AtlasBaseException
*/
void addOrUpdateNamespaceAttributes(String guid, Map<String, Map<String, Object>> namespaceAttributes, boolean isOverwrite) throws AtlasBaseException;
/**
*
* @param guid
* @param namespaceAttributes
* @throws AtlasBaseException
*/
void removeNamespaceAttributes(String guid, Map<String, Map<String, Object>> namespaceAttributes) throws AtlasBaseException;
/**
* Remove given labels, if labels is null/empty, no labels will be removed. If any labels in
* labels set are non-existing labels, they will be ignored, only existing labels will be removed.
*/
......
......@@ -47,6 +47,7 @@ import org.apache.atlas.repository.store.graph.v1.DeleteHandlerDelegate;
import org.apache.atlas.store.DeleteType;
import org.apache.atlas.type.AtlasClassificationType;
import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasNamespaceType.AtlasNamespaceAttribute;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
import org.apache.atlas.type.AtlasType;
import org.apache.atlas.type.AtlasTypeRegistry;
......@@ -74,6 +75,7 @@ import static java.lang.Boolean.FALSE;
import static org.apache.atlas.model.instance.EntityMutations.EntityOperation.*;
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.getTypeName;
import static org.apache.atlas.repository.graph.GraphHelper.isEntityIncomplete;
import static org.apache.atlas.repository.store.graph.v2.EntityGraphMapper.validateLabels;
......@@ -318,13 +320,13 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
@Override
@GraphTransaction
public EntityMutationResponse createOrUpdate(EntityStream entityStream, boolean isPartialUpdate) throws AtlasBaseException {
return createOrUpdate(entityStream, isPartialUpdate, false);
return createOrUpdate(entityStream, isPartialUpdate, false, false);
}
@Override
@GraphTransaction(logRollback = false)
public EntityMutationResponse createOrUpdateForImport(EntityStream entityStream) throws AtlasBaseException {
return createOrUpdate(entityStream, false, true);
return createOrUpdate(entityStream, false, true, true);
}
@Override
......@@ -356,7 +358,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
entity.setGuid(guid);
return createOrUpdate(new AtlasEntityStream(updatedEntityInfo), isPartialUpdate, false);
return createOrUpdate(new AtlasEntityStream(updatedEntityInfo), isPartialUpdate, false, false);
}
@Override
......@@ -378,7 +380,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_UPDATE, new AtlasEntityHeader(entity)), "update entity ByUniqueAttributes");
return createOrUpdate(new AtlasEntityStream(updatedEntityInfo), true, false);
return createOrUpdate(new AtlasEntityStream(updatedEntityInfo), true, false, false);
}
@Override
......@@ -429,7 +431,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
throw new AtlasBaseException(AtlasErrorCode.ATTRIBUTE_UPDATE_NOT_SUPPORTED, attrName, attrType.getTypeName());
}
return createOrUpdate(new AtlasEntityStream(updateEntity), true, false);
return createOrUpdate(new AtlasEntityStream(updateEntity), true, false, false);
}
@Override
......@@ -823,6 +825,66 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
@Override
@GraphTransaction
public void addOrUpdateNamespaceAttributes(String guid, Map<String, Map<String, Object>> entityNamespaces, boolean isOverwrite) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> addOrUpdateNamespaceAttributes(guid={}, entityNamespaces={}, isOverwrite={})", guid, entityNamespaces, isOverwrite);
}
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);
}
String typeName = getTypeName(entityVertex);
AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);
validateNamespaceAttributes(entityVertex, entityType, entityNamespaces, isOverwrite);
if (isOverwrite) {
entityGraphMapper.setNamespaceAttributes(entityVertex, entityType, entityNamespaces);
} else {
entityGraphMapper.addOrUpdateNamespaceAttributes(entityVertex, entityType, entityNamespaces);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== addOrUpdateNamespaceAttributes(guid={}, entityNamespaces={}, isOverwrite={})", guid, entityNamespaces, isOverwrite);
}
}
@Override
@GraphTransaction
public void removeNamespaceAttributes(String guid, Map<String, Map<String, Object>> entityNamespaces) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> removeNamespaceAttributes(guid={}, entityNamespaces={})", guid, entityNamespaces);
}
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);
}
String typeName = getTypeName(entityVertex);
AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);
entityGraphMapper.removeNamespaceAttributes(entityVertex, entityType, entityNamespaces);
if (LOG.isDebugEnabled()) {
LOG.debug("<== removeNamespaceAttributes(guid={}, entityNamespaces={})", guid, entityNamespaces);
}
}
@Override
@GraphTransaction
public void setLabels(String guid, Set<String> labels) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> setLabels()");
......@@ -899,7 +961,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
}
}
private EntityMutationResponse createOrUpdate(EntityStream entityStream, boolean isPartialUpdate, boolean replaceClassifications) throws AtlasBaseException {
private EntityMutationResponse createOrUpdate(EntityStream entityStream, boolean isPartialUpdate, boolean replaceClassifications, boolean replaceNamespaceAttributes) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> createOrUpdate()");
}
......@@ -1040,7 +1102,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
RequestContext.get().endMetricRecord(checkForUnchangedEntities);
}
EntityMutationResponse ret = entityGraphMapper.mapAttributesAndClassifications(context, isPartialUpdate, replaceClassifications);
EntityMutationResponse ret = entityGraphMapper.mapAttributesAndClassifications(context, isPartialUpdate, replaceClassifications, replaceNamespaceAttributes);
ret.setGuidAssignments(context.getGuidAssignments());
......@@ -1311,4 +1373,50 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore {
}
}
}
private void validateNamespaceAttributes(AtlasVertex entityVertex, AtlasEntityType entityType, Map<String, Map<String, Object>> entityNamespaces, boolean isOverwrite) throws AtlasBaseException {
List<String> messages = new ArrayList<>();
Map<String, List<AtlasNamespaceAttribute>> entityTypeNamespaces = entityType.getNamespaceAttributes();
for (String nsName : entityNamespaces.keySet()) {
if (!entityNamespaces.containsKey(nsName)) {
messages.add(nsName + ": invalid namespace for entity type " + entityType.getTypeName());
continue;
}
List<AtlasNamespaceAttribute> entityTypeNsAttributes = entityTypeNamespaces.get(nsName);
Map<String, Object> entityNsAttributes = entityNamespaces.get(nsName);
for (AtlasNamespaceAttribute nsAttribute : entityTypeNsAttributes) {
AtlasType attrType = nsAttribute.getAttributeType();
String attrName = nsAttribute.getName();
Object attrValue = entityNsAttributes.get(attrName);
String fieldName = entityType.getTypeName() + "." + nsName + "." + attrName;
if (attrValue != null) {
attrType.validateValue(attrValue, fieldName, messages);
} else if (!nsAttribute.getAttributeDef().getIsOptional()) {
final boolean isAttrValuePresent;
if (isOverwrite) {
isAttrValuePresent = false;
} else {
Object existingValue = AtlasGraphUtilsV2.getEncodedProperty(entityVertex, nsAttribute.getVertexPropertyName(), Object.class);
isAttrValuePresent = existingValue != null;
}
if (!isAttrValuePresent) {
messages.add(fieldName + ": mandatory namespace attribute value missing in type " + entityType.getTypeName());
}
}
}
}
if (!messages.isEmpty()) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_CRUD_INVALID_PARAMS, messages);
}
}
}
......@@ -53,6 +53,7 @@ import org.apache.atlas.type.AtlasBuiltInTypes;
import org.apache.atlas.type.AtlasClassificationType;
import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasMapType;
import org.apache.atlas.type.AtlasNamespaceType.AtlasNamespaceAttribute;
import org.apache.atlas.type.AtlasStructType;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute.AtlasRelationshipEdgeDirection;
......@@ -253,7 +254,7 @@ public class EntityGraphMapper {
}
}
public EntityMutationResponse mapAttributesAndClassifications(EntityMutationContext context, final boolean isPartialUpdate, final boolean replaceClassifications) throws AtlasBaseException {
public EntityMutationResponse mapAttributesAndClassifications(EntityMutationContext context, final boolean isPartialUpdate, final boolean replaceClassifications, boolean replaceNamespaceAttributes) throws AtlasBaseException {
MetricRecorder metric = RequestContext.get().startMetricRecord("mapAttributesAndClassifications");
EntityMutationResponse resp = new EntityMutationResponse();
......@@ -275,6 +276,8 @@ public class EntityGraphMapper {
resp.addEntity(CREATE, constructHeader(createdEntity, entityType, vertex));
addClassifications(context, guid, createdEntity.getClassifications());
addOrUpdateNamespaceAttributes(vertex, entityType, createdEntity.getNamespaceAttributes());
reqContext.cache(createdEntity);
}
}
......@@ -300,6 +303,10 @@ public class EntityGraphMapper {
addClassifications(context, guid, updatedEntity.getClassifications());
}
if (replaceNamespaceAttributes) {
setNamespaceAttributes(vertex, entityType, updatedEntity.getNamespaceAttributes());
}
reqContext.cache(updatedEntity);
}
}
......@@ -412,6 +419,143 @@ public class EntityGraphMapper {
return ret;
}
/*
* reset/overwrite namespace attributes of the entity with given values
*/
public void setNamespaceAttributes(AtlasVertex entityVertex, AtlasEntityType entityType, Map<String, Map<String, Object>> entityNamespaces) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> setNamespaceAttributes(entityVertex={}, entityType={}, entityNamespaces={}", entityVertex, entityType.getTypeName(), entityNamespaces);
}
Map<String, List<AtlasNamespaceAttribute>> entityTypeNamespaces = entityType.getNamespaceAttributes();
for (Map.Entry<String, List<AtlasNamespaceAttribute>> entry : entityTypeNamespaces.entrySet()) {
String nsName = entry.getKey();
List<AtlasNamespaceAttribute> entityTypeNsAttributes = entry.getValue();
Map<String, Object> entityNsAttributes = MapUtils.isEmpty(entityNamespaces) ? null : entityNamespaces.get(nsName);
for (AtlasNamespaceAttribute nsAttribute : entityTypeNsAttributes) {
String nsAttrName = nsAttribute.getName();
Object nsAttrExistingValue = entityVertex.getProperty(nsAttribute.getVertexPropertyName(), Object.class);
Object nsAttrNewValue = MapUtils.isEmpty(entityNsAttributes) ? null : entityNsAttributes.get(nsAttrName);
if (nsAttrExistingValue == null) {
if (nsAttrNewValue != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("setNamespaceAttributes(): adding {}.{}={}", nsName, nsAttribute.getName(), nsAttrNewValue);
}
mapAttribute(nsAttribute, nsAttrNewValue, entityVertex, CREATE, new EntityMutationContext());
}
} else {
if (nsAttrNewValue != null) {
if (!Objects.equals(nsAttrExistingValue, nsAttrNewValue)) {
if (LOG.isDebugEnabled()) {
LOG.debug("setNamespaceAttributes(): updating {}.{}={}", nsName, nsAttribute.getName(), nsAttrNewValue);
}
mapAttribute(nsAttribute, nsAttrNewValue, entityVertex, UPDATE, new EntityMutationContext());
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("setNamespaceAttributes(): removing {}.{}", nsName, nsAttribute.getName());
}
entityVertex.removeProperty(nsAttribute.getVertexPropertyName());
}
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== setNamespaceAttributes(entityVertex={}, entityType={}, entityNamespaces={}", entityVertex, entityType.getTypeName(), entityNamespaces);
}
}
/*
* add or update the given namespace attributes on the entity
*/
public void addOrUpdateNamespaceAttributes(AtlasVertex entityVertex, AtlasEntityType entityType, Map<String, Map<String, Object>> entityNamespaces) throws AtlasBaseException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> addOrUpdateNamespaceAttributes(entityVertex={}, entityType={}, entityNamespaces={}", entityVertex, entityType.getTypeName(), entityNamespaces);
}
Map<String, List<AtlasNamespaceAttribute>> entityTypeNamespaces = entityType.getNamespaceAttributes();
if (MapUtils.isNotEmpty(entityTypeNamespaces) && MapUtils.isNotEmpty(entityNamespaces)) {
for (Map.Entry<String, List<AtlasNamespaceAttribute>> entry : entityTypeNamespaces.entrySet()) {
String nsName = entry.getKey();
List<AtlasNamespaceAttribute> entityTypeNsAttributes = entry.getValue();
Map<String, Object> entityNsAttributes = entityNamespaces.get(nsName);
if (MapUtils.isEmpty(entityNsAttributes)) {
continue;
}
for (AtlasNamespaceAttribute nsAttribute : entityTypeNsAttributes) {
String nsAttrName = nsAttribute.getName();
if (!entityNsAttributes.containsKey(nsAttrName)) {
continue;
}
Object nsAttrValue = entityNsAttributes.get(nsAttrName);
Object existingValue = AtlasGraphUtilsV2.getEncodedProperty(entityVertex, nsAttribute.getVertexPropertyName(), Object.class);
if (existingValue == null) {
if (nsAttrValue != null) {
mapAttribute(nsAttribute, nsAttrValue, entityVertex, CREATE, new EntityMutationContext());
}
} else {
if (!Objects.equals(existingValue, nsAttrValue)) {
mapAttribute(nsAttribute, nsAttrValue, entityVertex, UPDATE, new EntityMutationContext());
}
}
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== addOrUpdateNamespaceAttributes(entityVertex={}, entityType={}, entityNamespaces={}", entityVertex, entityType.getTypeName(), entityNamespaces);
}
}
/*
* remove the given namespace attributes from the entity
*/
public void removeNamespaceAttributes(AtlasVertex entityVertex, AtlasEntityType entityType, Map<String, Map<String, Object>> entityNamespaces) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> removeNamespaceAttributes(entityVertex={}, entityType={}, entityNamespaces={}", entityVertex, entityType.getTypeName(), entityNamespaces);
}
Map<String, List<AtlasNamespaceAttribute>> entityTypeNamespaces = entityType.getNamespaceAttributes();
if (MapUtils.isNotEmpty(entityTypeNamespaces) && MapUtils.isNotEmpty(entityNamespaces)) {
for (Map.Entry<String, List<AtlasNamespaceAttribute>> entry : entityTypeNamespaces.entrySet()) {
String nsName = entry.getKey();
List<AtlasNamespaceAttribute> entityTypeNsAttributes = entry.getValue();
if (!entityNamespaces.containsKey(nsName)) { // nothing to remove for this namespace
continue;
}
Map<String, Object> entityNsAttributes = entityNamespaces.get(nsName);
for (AtlasNamespaceAttribute nsAttribute : entityTypeNsAttributes) {
// if (entityNsAttributes is empty) remove all attributes in this namespace
// else remove the attribute only if its given in entityNsAttributes
if (MapUtils.isEmpty(entityNsAttributes) || entityNsAttributes.containsKey(nsAttribute.getName())) {
entityVertex.removeProperty(nsAttribute.getVertexPropertyName());
}
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== removeNamespaceAttributes(entityVertex={}, entityType={}, entityNamespaces={}", entityVertex, entityType.getTypeName(), entityNamespaces);
}
}
private AtlasVertex createStructVertex(AtlasStruct struct) {
return createStructVertex(struct.getTypeName());
}
......
......@@ -48,6 +48,7 @@ import org.apache.atlas.type.AtlasArrayType;
import org.apache.atlas.type.AtlasBuiltInTypes.AtlasObjectIdType;
import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasMapType;
import org.apache.atlas.type.AtlasNamespaceType.AtlasNamespaceAttribute;
import org.apache.atlas.type.AtlasRelationshipType;
import org.apache.atlas.type.AtlasStructType;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
......@@ -586,6 +587,8 @@ public class EntityGraphRetriever {
mapSystemAttributes(entityVertex, entity);
mapNamespaceAttributes(entityVertex, entity);
mapAttributes(entityVertex, entity, entityExtInfo, isMinExtInfo, includeReferences);
if (!ignoreRelationshipAttr) { // only map when really needed
......@@ -761,6 +764,26 @@ public class EntityGraphRetriever {
}
}
private void mapNamespaceAttributes(AtlasVertex entityVertex, AtlasEntity entity) throws AtlasBaseException {
AtlasEntityType entityType = typeRegistry.getEntityTypeByName(entity.getTypeName());
Map<String, List<AtlasNamespaceAttribute>> entityTypeNamespaces = entityType != null ? entityType.getNamespaceAttributes() : null;
if (MapUtils.isNotEmpty(entityTypeNamespaces)) {
for (Map.Entry<String, List<AtlasNamespaceAttribute>> entry : entityTypeNamespaces.entrySet()) {
String nsName = entry.getKey();
List<AtlasNamespaceAttribute> nsAttributes = entry.getValue();
for (AtlasNamespaceAttribute nsAttribute : nsAttributes) {
Object nsAttrValue = mapVertexToAttribute(entityVertex, nsAttribute, null, false, false);
if (nsAttrValue != null) {
entity.setNamespaceAttribute(nsName, nsAttribute.getName(), nsAttrValue);
}
}
}
}
}
public List<AtlasClassification> getAllClassifications(AtlasVertex entityVertex) throws AtlasBaseException {
List<AtlasClassification> ret = new ArrayList<>();
Iterable edges = entityVertex.query().direction(AtlasEdgeDirection.OUT).label(CLASSIFICATION_LABEL).edges();
......
......@@ -64,6 +64,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -855,6 +856,78 @@ public class EntityREST {
}
}
@POST
@Path("/guid/{guid}/namespaces")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void addOrUpdateNamespaceAttributes(@PathParam("guid") final String guid, @QueryParam("isOverwrite") @DefaultValue("false") boolean isOverwrite, Map<String, Map<String, Object>> entityNamespaces) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addOrUpdateNamespaceAttributes(" + guid + ", isOverwrite=" + isOverwrite + ")");
}
entitiesStore.addOrUpdateNamespaceAttributes(guid, entityNamespaces, isOverwrite);
} finally {
AtlasPerfTracer.log(perf);
}
}
@DELETE
@Path("/guid/{guid}/namespaces")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void removeNamespaceAttributes(@PathParam("guid") final String guid, Map<String, Map<String, Object>> entityNamespaces) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.removeNamespaceAttributes(" + guid + ")");
}
entitiesStore.removeNamespaceAttributes(guid, entityNamespaces);
} finally {
AtlasPerfTracer.log(perf);
}
}
@POST
@Path("/guid/{guid}/namespace/{namespace}")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void addOrUpdateNamespaceAttributes(@PathParam("guid") final String guid, @PathParam("namespace") final String namespace, Map<String, Object> entityNsAttributes) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addOrUpdateNamespaceAttributes(" + guid + ", " + namespace + ")");
}
entitiesStore.addOrUpdateNamespaceAttributes(guid, Collections.singletonMap(namespace, entityNsAttributes), false);
} finally {
AtlasPerfTracer.log(perf);
}
}
@DELETE
@Path("/guid/{guid}/namespace/{namespace}")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void removeNamespaceAttributes(@PathParam("guid") final String guid, @PathParam("namespace") final String namespace, Map<String, Object> entityNsAttributes) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.removeNamespaceAttributes(" + guid + ", " + namespace + ")");
}
entitiesStore.removeNamespaceAttributes(guid, Collections.singletonMap(namespace, entityNsAttributes));
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Set labels to a given entity
* @param guid - Unique entity identifier
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment