ATLAS-1856: AtlasRelationship instance implementation Java/REST
Signed-off-by: Madhan Neethiraj <madhan@apache.org>
diff --git a/authorization/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java b/authorization/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
index deccf84..925b6b1 100644
--- a/authorization/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
+++ b/authorization/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
@@ -19,5 +19,5 @@
package org.apache.atlas.authorize;
public enum AtlasResourceTypes {
- UNKNOWN, ENTITY, TYPE, OPERATION, TAXONOMY, TERM
+ UNKNOWN, ENTITY, TYPE, OPERATION, TAXONOMY, TERM, RELATION
}
diff --git a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasAuthorizationUtils.java b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasAuthorizationUtils.java
index 93d988e..bb3157a 100644
--- a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasAuthorizationUtils.java
+++ b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasAuthorizationUtils.java
@@ -139,6 +139,8 @@
if (contextPath.contains("/terms")) {
resourceTypes.add(AtlasResourceTypes.TERM);
}
+ } else if (api.startsWith("relation")) {
+ resourceTypes.add(AtlasResourceTypes.RELATION);
} else {
LOG.error("Unable to find Atlas Resource corresponding to : {}\nSetting {}"
, api, AtlasResourceTypes.UNKNOWN.name());
diff --git a/authorization/src/main/java/org/apache/atlas/authorize/simple/PolicyParser.java b/authorization/src/main/java/org/apache/atlas/authorize/simple/PolicyParser.java
index 7ef49e6..acf7388 100644
--- a/authorization/src/main/java/org/apache/atlas/authorize/simple/PolicyParser.java
+++ b/authorization/src/main/java/org/apache/atlas/authorize/simple/PolicyParser.java
@@ -231,6 +231,8 @@
resourceType = AtlasResourceTypes.TAXONOMY;
} else if (type.equalsIgnoreCase("TERM")) {
resourceType = AtlasResourceTypes.TERM;
+ } else if (type.equalsIgnoreCase("RELATION")) {
+ resourceType = AtlasResourceTypes.RELATION;
} else {
Log.warn(type + " is invalid resource please check PolicyStore file");
continue;
diff --git a/graphdb/api/src/main/java/org/apache/atlas/repository/graphdb/AtlasGraphQuery.java b/graphdb/api/src/main/java/org/apache/atlas/repository/graphdb/AtlasGraphQuery.java
index bd7b35e..841edf7 100644
--- a/graphdb/api/src/main/java/org/apache/atlas/repository/graphdb/AtlasGraphQuery.java
+++ b/graphdb/api/src/main/java/org/apache/atlas/repository/graphdb/AtlasGraphQuery.java
@@ -60,6 +60,12 @@
*/
Iterable<AtlasVertex<V, E>> vertices();
+ /**
+ * Executes the query and returns the matching edges.
+ * @return
+ */
+ Iterable<AtlasEdge<V, E>> edges();
+
/**
* Adds a predicate that the returned vertices must have the specified
diff --git a/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/NativeTitanGraphQuery.java b/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/NativeTitanGraphQuery.java
index 662a270..0211ff0 100644
--- a/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/NativeTitanGraphQuery.java
+++ b/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/NativeTitanGraphQuery.java
@@ -19,6 +19,7 @@
import java.util.Collection;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasGraphQuery.ComparisionOperator;
import org.apache.atlas.repository.graphdb.AtlasVertex;
@@ -39,6 +40,11 @@
*/
Iterable<AtlasVertex<V, E>> vertices();
+ /**
+ * Executes the graph query.
+ * @return
+ */
+ Iterable<AtlasEdge<V, E>> edges();
/**
* Adds an in condition to the query.
diff --git a/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/TitanGraphQuery.java b/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/TitanGraphQuery.java
index 056088c..0077a21 100644
--- a/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/TitanGraphQuery.java
+++ b/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/TitanGraphQuery.java
@@ -22,6 +22,7 @@
import java.util.List;
import java.util.Set;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.repository.graphdb.AtlasGraphQuery;
import org.apache.atlas.repository.graphdb.AtlasVertex;
@@ -121,8 +122,10 @@
@Override
public Iterable<AtlasVertex<V, E>> vertices() {
- LOG.debug("Executing: ");
- LOG.debug(queryCondition.toString());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Executing: " + queryCondition.toString());
+ }
+
//compute the overall result by unioning the results from all of the
//AndConditions together.
Set<AtlasVertex<V, E>> result = new HashSet<>();
@@ -136,6 +139,24 @@
}
@Override
+ public Iterable<AtlasEdge<V, E>> edges() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Executing: " + queryCondition.toString());
+ }
+
+ //compute the overall result by unioning the results from all of the
+ //AndConditions together.
+ Set<AtlasEdge<V, E>> result = new HashSet<>();
+ for(AndCondition andExpr : queryCondition.getAndTerms()) {
+ NativeTitanGraphQuery<V, E> andQuery = andExpr.create(getQueryFactory());
+ for(AtlasEdge<V, E> edge : andQuery.edges()) {
+ result.add(edge);
+ }
+ }
+ return result;
+ }
+
+ @Override
public AtlasGraphQuery<V, E> has(String propertyKey, ComparisionOperator operator,
Object value) {
queryCondition.andWith(new HasPredicate(propertyKey, operator, value));
diff --git a/graphdb/titan0/src/main/java/org/apache/atlas/repository/graphdb/titan0/query/NativeTitan0GraphQuery.java b/graphdb/titan0/src/main/java/org/apache/atlas/repository/graphdb/titan0/query/NativeTitan0GraphQuery.java
index 5ad176b..7ec6ffe 100644
--- a/graphdb/titan0/src/main/java/org/apache/atlas/repository/graphdb/titan0/query/NativeTitan0GraphQuery.java
+++ b/graphdb/titan0/src/main/java/org/apache/atlas/repository/graphdb/titan0/query/NativeTitan0GraphQuery.java
@@ -19,6 +19,7 @@
import java.util.Collection;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasGraphQuery.ComparisionOperator;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.repository.graphdb.titan.query.NativeTitanGraphQuery;
@@ -54,6 +55,11 @@
return graph.wrapVertices(it);
}
+ @Override
+ public Iterable<AtlasEdge<Titan0Vertex, Titan0Edge>> edges() {
+ Iterable it = query.edges();
+ return graph.wrapEdges(it);
+ }
@Override
public void in(String propertyName, Collection<?> values) {
diff --git a/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/Titan1Graph.java b/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/Titan1Graph.java
index e829d91..ffb6b37 100644
--- a/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/Titan1Graph.java
+++ b/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/Titan1Graph.java
@@ -384,11 +384,6 @@
return expr;
}
- public Iterable<AtlasEdge<Titan1Vertex, Titan1Edge>> wrapEdges(Iterator<Edge> it) {
- Iterable<Edge> iterable = new IteratorToIterableAdapter<Edge>(it);
- return wrapEdges(iterable);
- }
-
public Iterable<AtlasVertex<Titan1Vertex, Titan1Edge>> wrapVertices(Iterator<? extends Vertex> it) {
Iterable<? extends Vertex> iterable = new IteratorToIterableAdapter<>(it);
return wrapVertices(iterable);
@@ -406,15 +401,21 @@
}
- public Iterable<AtlasEdge<Titan1Vertex, Titan1Edge>> wrapEdges(Iterable<Edge> it) {
- Iterable<Edge> result = (Iterable<Edge>) it;
- return Iterables.transform(result, new Function<Edge, AtlasEdge<Titan1Vertex, Titan1Edge>>() {
+ public Iterable<AtlasEdge<Titan1Vertex, Titan1Edge>> wrapEdges(Iterator<? extends Edge> it) {
+ Iterable<? extends Edge> iterable = new IteratorToIterableAdapter<>(it);
+ return wrapEdges(iterable);
+ }
+
+ public Iterable<AtlasEdge<Titan1Vertex, Titan1Edge>> wrapEdges(Iterable<? extends Edge> it) {
+
+ return Iterables.transform(it, new Function<Edge, AtlasEdge<Titan1Vertex, Titan1Edge>>() {
@Override
public AtlasEdge<Titan1Vertex, Titan1Edge> apply(Edge input) {
return GraphDbObjectFactory.createEdge(Titan1Graph.this, input);
}
});
+
}
@Override
diff --git a/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/query/NativeTitan1GraphQuery.java b/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/query/NativeTitan1GraphQuery.java
index 9dc175b..1ca900d 100644
--- a/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/query/NativeTitan1GraphQuery.java
+++ b/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/query/NativeTitan1GraphQuery.java
@@ -19,6 +19,8 @@
import java.util.Collection;
+import com.thinkaurelius.titan.core.TitanEdge;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasGraphQuery.ComparisionOperator;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.repository.graphdb.titan.query.NativeTitanGraphQuery;
@@ -53,6 +55,12 @@
}
@Override
+ public Iterable<AtlasEdge<Titan1Vertex, Titan1Edge>> edges() {
+ Iterable<TitanEdge> it = query.edges();
+ return graph.wrapEdges(it);
+ }
+
+ @Override
public void in(String propertyName, Collection<? extends Object> values) {
query.has(propertyName, Contain.IN, values);
diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index 6c33f40..e8971a8 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -79,6 +79,8 @@
RELATIONSHIPDEF_AGGREGATION_NO_CONTAINER(400, "ATLAS-400-00-032", "AGGREGATION relationshipDef {0} creation attempted without an end specifying isContainer"),
RELATIONSHIPDEF_COMPOSITION_SET_CONTAINER(400, "ATLAS-400-00-033", "COMPOSITION relationshipDef {0} cannot have a SET cardinality and be a container"),
RELATIONSHIPDEF_LIST_ON_END(400, "ATLAS-400-00-034", "relationshipDef {0} cannot have a LIST cardinality on an end"),
+ RELATIONSHIPDEF_INVALID_END_TYPE(400, "ATLAS-400-00-035", "relationshipDef {0} has invalid end type {1}"),
+ INVALID_RELATIONSHIP_END_TYPE(400, "ATLAS-400-00-036", "invalid end type for relationship {0}: expected {1}, found {2}"),
// All Not found enums go here
TYPE_NAME_NOT_FOUND(404, "ATLAS-404-00-001", "Given typename {0} was invalid"),
TYPE_GUID_NOT_FOUND(404, "ATLAS-404-00-002", "Given type guid {0} was invalid"),
@@ -90,11 +92,13 @@
INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND(404, "ATLAS-404-00-009", "Instance {0} with unique attribute {1} does not exist"),
REFERENCED_ENTITY_NOT_FOUND(404, "ATLAS-404-00-00A", "Referenced entity {0} is not found"),
INSTANCE_NOT_FOUND(404, "ATLAS-404-00-00B", "Given instance is invalid/not found: {0}"),
+ RELATIONSHIP_GUID_NOT_FOUND(404, "ATLAS-404-00-00C", "Given relationship guid {0} is invalid/not found"),
// All data conflict errors go here
TYPE_ALREADY_EXISTS(409, "ATLAS-409-00-001", "Given type {0} already exists"),
TYPE_HAS_REFERENCES(409, "ATLAS-409-00-002", "Given type {0} has references"),
INSTANCE_ALREADY_EXISTS(409, "ATLAS-409-00-003", "failed to update entity: {0}"),
+ RELATIONSHIP_ALREADY_EXISTS(409, "ATLAS-409-00-004", "relationship {0} already exists between entities {1} and {2}"),
// All internal errors go here
INTERNAL_ERROR(500, "ATLAS-500-00-001", "Internal server error {0}"),
diff --git a/intg/src/main/java/org/apache/atlas/model/instance/AtlasRelationship.java b/intg/src/main/java/org/apache/atlas/model/instance/AtlasRelationship.java
new file mode 100644
index 0000000..8d2e7ec
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/model/instance/AtlasRelationship.java
@@ -0,0 +1,243 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.atlas.model.instance;
+
+import org.apache.atlas.model.typedef.AtlasRelationshipDef;
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
+
+
+/**
+ * Atlas relationship instance.
+ */
+@JsonAutoDetect(getterVisibility=PUBLIC_ONLY, setterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class AtlasRelationship extends AtlasStruct implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String guid = null;
+ private AtlasObjectId end1 = null;
+ private AtlasObjectId end2 = null;
+ private Status status = Status.ACTIVE;
+ private String createdBy = null;
+ private String updatedBy = null;
+ private Date createTime = null;
+ private Date updateTime = null;
+ private Long version = 0L;
+
+ public enum Status { ACTIVE, DELETED }
+
+ @JsonIgnore
+ private static AtomicLong s_nextId = new AtomicLong(System.nanoTime());
+
+ public AtlasRelationship() {
+ super();
+
+ init();
+ }
+
+ public AtlasRelationship(String typeName) {
+ this(typeName, null);
+ }
+
+ public AtlasRelationship(String typeName, Map<String, Object> attributes) {
+ super(typeName, attributes);
+
+ init();
+ }
+
+ public AtlasRelationship(String typeName, AtlasObjectId end1, AtlasObjectId end2) {
+ super(typeName);
+
+ init(nextInternalId(), end1, end2, null, null, null, null, null, 0L);
+ }
+
+ public AtlasRelationship(String typeName, String attrName, Object attrValue) {
+ super(typeName, attrName, attrValue);
+
+ init();
+ }
+
+ public AtlasRelationship(AtlasRelationshipDef relationshipDef) {
+ this(relationshipDef != null ? relationshipDef.getName() : null);
+ }
+
+ public AtlasRelationship(AtlasRelationship other) {
+ super(other);
+
+ if (other != null) {
+ init(other.guid, other.end1, other.end2, other.status, other.createdBy, other.updatedBy,
+ other.createTime, other.updateTime, other.version);
+ }
+ }
+
+ public String getGuid() {
+ return guid;
+ }
+
+ public void setGuid(String guid) {
+ this.guid = guid;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public String getUpdatedBy() {
+ return updatedBy;
+ }
+
+ public void setUpdatedBy(String updatedBy) {
+ this.updatedBy = updatedBy;
+ }
+
+ public Date getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime) {
+ this.createTime = createTime;
+ }
+
+ public Date getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public AtlasObjectId getEnd1() { return end1; }
+
+ public void setEnd1(AtlasObjectId end1) { this.end1 = end1; }
+
+ public AtlasObjectId getEnd2() { return end2; }
+
+ public void setEnd2(AtlasObjectId end2) { this.end2 = end2; }
+
+ private static String nextInternalId() {
+ return "-" + Long.toString(s_nextId.getAndIncrement());
+ }
+
+ public String getRelationshipLabel() { return "r:" + super.getTypeName(); }
+
+ private void init() {
+ init(nextInternalId(), null, null, null, null, null, null, null, 0L);
+ }
+
+ private void init(String guid, AtlasObjectId end1, AtlasObjectId end2,
+ Status status, String createdBy, String updatedBy,
+ Date createTime, Date updateTime, Long version) {
+ setGuid(guid);
+ setEnd1(end1);
+ setEnd2(end2);
+ setStatus(status);
+ setCreatedBy(createdBy);
+ setUpdatedBy(updatedBy);
+ setCreateTime(createTime);
+ setUpdateTime(updateTime);
+ setVersion(version);
+ }
+
+ @Override
+ public StringBuilder toString(StringBuilder sb) {
+ if (sb == null) {
+ sb = new StringBuilder();
+ }
+
+ sb.append("AtlasRelationship{");
+ super.toString(sb);
+ sb.append("guid='").append(guid).append('\'');
+ sb.append(", end1=").append(end1);
+ sb.append(", end2=").append(end2);
+ sb.append(", status=").append(status);
+ sb.append(", createdBy='").append(createdBy).append('\'');
+ sb.append(", updatedBy='").append(updatedBy).append('\'');
+ dumpDateField(", createTime=", createTime, sb);
+ dumpDateField(", updateTime=", updateTime, sb);
+ sb.append(", version=").append(version);
+ sb.append('}');
+
+ return sb;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+ if (!super.equals(o)) { return false; }
+
+ AtlasRelationship that = (AtlasRelationship) o;
+ return Objects.equals(guid, that.guid) &&
+ Objects.equals(end1, that.end1) &&
+ Objects.equals(end2, that.end2) &&
+ status == that.status &&
+ Objects.equals(createdBy, that.createdBy) &&
+ Objects.equals(updatedBy, that.updatedBy) &&
+ Objects.equals(createTime, that.createTime) &&
+ Objects.equals(updateTime, that.updateTime) &&
+ Objects.equals(version, that.version);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), guid, end1, end2, status, createdBy, updatedBy, createTime, updateTime, version);
+ }
+
+ @Override
+ public String toString() {
+ return toString(new StringBuilder()).toString();
+ }
+}
\ No newline at end of file
diff --git a/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java b/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
index 000d747..34e932e 100644
--- a/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
+++ b/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
@@ -18,6 +18,7 @@
package org.apache.atlas.model.typedef;
import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef.Cardinality;
+import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
@@ -59,6 +60,10 @@
* This is the cardinality of the end
*/
private Cardinality cardinality;
+ /**
+ * legacy edge label name of the endpoint
+ */
+ private String legacyLabel;
/**
* Base constructor
@@ -92,10 +97,29 @@
* - whether the end is a container or not
*/
public AtlasRelationshipEndDef(String typeName, String name, Cardinality cardinality, boolean isContainer) {
+ this(typeName, name, cardinality, isContainer, null);
+ }
+
+ public AtlasRelationshipEndDef(String typeName, String name, Cardinality cardinality, boolean isContainer, String legacyLabel) {
setType(typeName);
setName(name);
setCardinality(cardinality);
setIsContainer(isContainer);
+ setLegacyLabel(legacyLabel);
+ }
+
+ /**
+ * Construct using an existing AtlasRelationshipEndDef
+ * @param other
+ */
+ public AtlasRelationshipEndDef(AtlasRelationshipEndDef other) {
+ if (other != null) {
+ setType(other.getType());
+ setName(other.getName());
+ setIsContainer(other.getIsContainer());
+ setCardinality(other.getCardinality());
+ setLegacyLabel(other.getLegacyLabel());
+ }
}
public void setType(String type) {
@@ -142,18 +166,15 @@
return this.cardinality;
}
- /**
- * Construct using an existing AtlasRelationshipEndDef
- * @param other
- */
- public AtlasRelationshipEndDef(AtlasRelationshipEndDef other) {
- if (other != null) {
- setType(other.getType());
- setName(other.getName());
- setIsContainer(other.getIsContainer());
- setCardinality(other.getCardinality());
- }
- }
+ public boolean isContainer() { return isContainer; }
+
+ public void setContainer(boolean container) { isContainer = container; }
+
+ public String getLegacyLabel() { return legacyLabel; }
+
+ public void setLegacyLabel(String legacyLabel) { this.legacyLabel = legacyLabel; }
+
+ public boolean hasLegacyRelation() { return StringUtils.isNotEmpty(getLegacyLabel()) ? true : false; }
public StringBuilder toString(StringBuilder sb) {
if (sb == null) {
@@ -165,6 +186,7 @@
sb.append(", name==>'").append(name).append('\'');
sb.append(", isContainer==>'").append(isContainer).append('\'');
sb.append(", cardinality==>'").append(cardinality).append('\'');
+ sb.append(", legacyLabel==>'").append(legacyLabel).append('\'');
sb.append('}');
return sb;
@@ -177,13 +199,17 @@
if (o == null || getClass() != o.getClass())
return false;
AtlasRelationshipEndDef that = (AtlasRelationshipEndDef) o;
- return Objects.equals(type, that.type) && Objects.equals(name, that.name)
- && (isContainer == that.isContainer) && (cardinality == that.cardinality);
+
+ return Objects.equals(type, that.type) &&
+ Objects.equals(name, that.name) &&
+ isContainer == that.isContainer &&
+ cardinality == that.cardinality &&
+ Objects.equals(legacyLabel, that.legacyLabel);
}
@Override
public int hashCode() {
- return Objects.hash(type, getName(), isContainer, cardinality);
+ return Objects.hash(type, getName(), isContainer, cardinality, legacyLabel);
}
@Override
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
index 0ff1582..a29f7fb 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
@@ -128,6 +128,10 @@
return StringUtils.isNotEmpty(entityTypeName) && allSubTypes.contains(entityTypeName);
}
+ public boolean isTypeOrSuperTypeOf(String entityTypeName) {
+ return StringUtils.isNotEmpty(entityTypeName) && typeAndAllSubTypes.contains(entityTypeName);
+ }
+
public boolean isSubTypeOf(AtlasEntityType entityType) {
return entityType != null && allSuperTypes.contains(entityType.getTypeName());
}
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java b/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java
index eb2fc48..296c06c 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java
@@ -35,6 +35,8 @@
private static final Logger LOG = LoggerFactory.getLogger(AtlasRelationshipType.class);
private final AtlasRelationshipDef relationshipDef;
+ private AtlasEntityType end1Type;
+ private AtlasEntityType end2Type;
public AtlasRelationshipType(AtlasRelationshipDef relationshipDef) {
super(relationshipDef);
@@ -55,6 +57,24 @@
public void resolveReferences(AtlasTypeRegistry typeRegistry) throws AtlasBaseException {
super.resolveReferences(typeRegistry);
+ String end1TypeName = relationshipDef != null && relationshipDef.getEndDef1() != null ? relationshipDef.getEndDef1().getType() : null;
+ String end2TypeName = relationshipDef != null && relationshipDef.getEndDef2() != null ? relationshipDef.getEndDef2().getType() : null;
+
+ AtlasType type1 = typeRegistry.getType(end1TypeName);
+ AtlasType type2 = typeRegistry.getType(end2TypeName);
+
+ if (type1 instanceof AtlasEntityType) {
+ end1Type = (AtlasEntityType)type1;
+ } else {
+ throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIPDEF_INVALID_END_TYPE, getTypeName(), end1TypeName);
+ }
+
+ if (type2 instanceof AtlasEntityType) {
+ end2Type = (AtlasEntityType)type2;
+ } else {
+ throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIPDEF_INVALID_END_TYPE, getTypeName(), end2TypeName);
+ }
+
validateAtlasRelationshipDef(this.relationshipDef);
}
@@ -81,6 +101,11 @@
return ret;
}
+
+ public AtlasEntityType getEnd1Type() { return end1Type; }
+
+ public AtlasEntityType getEnd2Type() { return end2Type; }
+
/**
* Validate the fields in the the RelationshipType are consistent with respect to themselves.
* @param type
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasTypeRegistry.java b/intg/src/main/java/org/apache/atlas/type/AtlasTypeRegistry.java
index aebd4d1..29fae1c 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasTypeRegistry.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasTypeRegistry.java
@@ -186,6 +186,7 @@
return registryData.classificationDefs.getTypeByName(name);
}
+ public Collection<AtlasRelationshipDef> getAllRelationshipDefs() { return registryData.relationshipDefs.getAll(); }
public Collection<AtlasEntityDef> getAllEntityDefs() { return registryData.entityDefs.getAll(); }
public AtlasEntityDef getEntityDefByGuid(String guid) {
@@ -211,6 +212,7 @@
return registryData.relationshipDefs.getTypeDefByName(name);
}
public AtlasRelationshipType getRelationshipTypeByName(String name) { return registryData.relationshipDefs.getTypeByName(name); }
+
public AtlasTransientTypeRegistry lockTypeRegistryForUpdate() throws AtlasBaseException {
return lockTypeRegistryForUpdate(DEFAULT_LOCK_MAX_WAIT_TIME_IN_SECONDS);
}
@@ -347,6 +349,7 @@
addTypesWithNoRefResolve(parent.getAllStructDefs());
addTypesWithNoRefResolve(parent.getAllClassificationDefs());
addTypesWithNoRefResolve(parent.getAllEntityDefs());
+ addTypesWithNoRefResolve(parent.getAllRelationshipDefs());
addedTypes.clear();
updatedTypes.clear();
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 ca7fad0..329dd7a 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
@@ -24,7 +24,9 @@
import org.apache.atlas.ApplicationProperties;
import org.apache.atlas.AtlasException;
import org.apache.atlas.RequestContext;
+import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.instance.AtlasEntity.Status;
+import org.apache.atlas.model.instance.AtlasRelationship;
import org.apache.atlas.repository.Constants;
import org.apache.atlas.repository.RepositoryException;
import org.apache.atlas.repository.graphdb.AtlasEdge;
@@ -259,31 +261,42 @@
* Searches for a AtlasVertex with prop1=key1 && prop2=key2
* @param args
* @return AtlasVertex with the given property keys
- * @throws EntityNotFoundException
+ * @throws AtlasBaseException
*/
public AtlasVertex findVertex(Object... args) throws EntityNotFoundException {
+ return (AtlasVertex) findElement(true, args);
+ }
+
+ /**
+ * Args of the format prop1, key1, prop2, key2...
+ * Searches for a AtlasEdge with prop1=key1 && prop2=key2
+ * @param args
+ * @return AtlasEdge with the given property keys
+ * @throws AtlasBaseException
+ */
+ public AtlasEdge findEdge(Object... args) throws EntityNotFoundException {
+ return (AtlasEdge) findElement(false, args);
+ }
+
+ private AtlasElement findElement(boolean isVertexSearch, Object... args) throws EntityNotFoundException {
AtlasGraphQuery query = graph.query();
- for (int i = 0 ; i < args.length; i+=2) {
- query = query.has((String) args[i], args[i+1]);
+
+ for (int i = 0; i < args.length; i += 2) {
+ query = query.has((String) args[i], args[i + 1]);
}
- Iterator<AtlasVertex> results = query.vertices().iterator();
- // returning one since entityType, qualifiedName should be unique
- AtlasVertex vertex = results.hasNext() ? results.next() : null;
+ Iterator<AtlasElement> results = isVertexSearch ? query.vertices().iterator() : query.edges().iterator();
+ AtlasElement element = (results != null && results.hasNext()) ? results.next() : null;
- if (vertex == null) {
- String conditionStr = getConditionString(args);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Could not find a vertex with {}", conditionStr);
- }
- throw new EntityNotFoundException("Could not find an entity in the repository with " + conditionStr);
- } else {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Found a vertex {} with {}", string(vertex), getConditionString(args));
- }
+ if (element == null) {
+ throw new EntityNotFoundException("Could not find " + (isVertexSearch ? "vertex" : "edge") + " with condition: " + getConditionString(args));
}
- return vertex;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found {} with condition {}", string(element), getConditionString(args));
+ }
+
+ return element;
}
//In some cases of parallel APIs, the edge is added, but get edge by label doesn't return the edge. ATLAS-1104
@@ -534,6 +547,9 @@
return findVertex(Constants.GUID_PROPERTY_KEY, guid);
}
+ public AtlasEdge getEdgeForGUID(String guid) throws EntityNotFoundException {
+ return findEdge(Constants.GUID_PROPERTY_KEY, guid);
+ }
/**
* Finds the Vertices that correspond to the given property values. Property
@@ -646,12 +662,12 @@
return getIdFromVertex(getTypeName(vertex), vertex);
}
- public static String getGuid(AtlasVertex vertex) {
- return vertex.<String>getProperty(Constants.GUID_PROPERTY_KEY, String.class);
+ public static String getGuid(AtlasElement element) {
+ return element.<String>getProperty(Constants.GUID_PROPERTY_KEY, String.class);
}
- public static String getTypeName(AtlasVertex instanceVertex) {
- return instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY, String.class);
+ public static String getTypeName(AtlasElement element) {
+ return element.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY, String.class);
}
public static Id.EntityState getState(AtlasElement element) {
@@ -663,8 +679,6 @@
return element.getProperty(Constants.VERSION_PROPERTY_KEY, Integer.class);
}
-
-
public static String getStateAsString(AtlasElement element) {
return element.getProperty(Constants.STATE_PROPERTY_KEY, String.class);
}
@@ -673,6 +687,10 @@
return (getState(element) == Id.EntityState.DELETED) ? Status.DELETED : Status.ACTIVE;
}
+ public static AtlasRelationship.Status getEdgeStatus(AtlasElement element) {
+ return (getState(element) == Id.EntityState.DELETED) ? AtlasRelationship.Status.DELETED : AtlasRelationship.Status.ACTIVE;
+ }
+
//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);
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasRelationshipStore.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasRelationshipStore.java
new file mode 100644
index 0000000..341711a
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasRelationshipStore.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.atlas.repository.store.graph;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasRelationship;
+
+/**
+ * Persistence/Retrieval API for AtlasRelationship
+ */
+public interface AtlasRelationshipStore {
+ /**
+ * Create a new relationship instance.
+ * @param relationship relationship instance definition
+ * @return AtlasRelationship d
+ */
+ AtlasRelationship create(AtlasRelationship relationship) throws AtlasBaseException;
+
+ /**
+ * Update an existing relationship instance.
+ * @param relationship relationship instance definition
+ * @return AtlasRelationship d
+ */
+ AtlasRelationship update(AtlasRelationship relationship) throws AtlasBaseException;
+
+ /**
+ * Retrieve a relationship instance using guid.
+ * @param guid relationship instance guid
+ * @return AtlasRelationship
+ */
+ AtlasRelationship getById(String guid) throws AtlasBaseException;
+
+ /**
+ * Delete a relationship instance using guid.
+ * @param guid relationship instance guid
+ */
+ void deleteById(String guid) throws AtlasBaseException;
+}
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasGraphUtilsV1.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasGraphUtilsV1.java
index 560b338..00fe94b 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasGraphUtilsV1.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasGraphUtilsV1.java
@@ -35,6 +35,7 @@
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
import org.apache.atlas.type.AtlasType;
import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -234,6 +235,18 @@
return vertex;
}
+ public static String getTypeNameFromGuid(String guid) {
+ String ret = null;
+
+ if (StringUtils.isNotEmpty(guid)) {
+ AtlasVertex vertex = AtlasGraphUtilsV1.findByGuid(guid);
+
+ ret = (vertex != null) ? AtlasGraphUtilsV1.getTypeName(vertex) : null;
+ }
+
+ return ret;
+ }
+
public static boolean typeHasInstanceVertex(String typeName) throws AtlasBaseException {
AtlasGraphQuery query = AtlasGraphProvider.getGraphInstance()
.query()
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1.java
new file mode 100644
index 0000000..8fe4888
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1.java
@@ -0,0 +1,365 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.atlas.repository.store.graph.v1;
+
+import org.apache.atlas.AtlasErrorCode;
+import org.apache.atlas.annotation.GraphTransaction;
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasObjectId;
+import org.apache.atlas.model.instance.AtlasRelationship;
+import org.apache.atlas.model.typedef.AtlasRelationshipDef;
+import org.apache.atlas.model.typedef.AtlasRelationshipEndDef;
+import org.apache.atlas.repository.Constants;
+import org.apache.atlas.repository.RepositoryException;
+import org.apache.atlas.repository.graph.GraphHelper;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
+import org.apache.atlas.repository.graphdb.AtlasVertex;
+import org.apache.atlas.repository.store.graph.AtlasRelationshipDefStore;
+import org.apache.atlas.repository.store.graph.AtlasRelationshipStore;
+import org.apache.atlas.type.AtlasEntityType;
+import org.apache.atlas.type.AtlasRelationshipType;
+import org.apache.atlas.type.AtlasStructType;
+import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
+import org.apache.atlas.type.AtlasType;
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.apache.atlas.type.AtlasTypeUtil;
+import org.apache.atlas.typesystem.exception.EntityNotFoundException;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.inject.Inject;
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+
+@Component
+public class AtlasRelationshipStoreV1 implements AtlasRelationshipStore {
+ private static final Logger LOG = LoggerFactory.getLogger(AtlasRelationshipStoreV1.class);
+
+ private final AtlasTypeRegistry typeRegistry;
+ private final EntityGraphRetriever entityRetriever;
+ private final GraphHelper graphHelper = GraphHelper.getInstance();
+
+ @Inject
+ public AtlasRelationshipStoreV1(AtlasTypeRegistry typeRegistry) {
+ this.typeRegistry = typeRegistry;
+ this.entityRetriever = new EntityGraphRetriever(typeRegistry);
+ }
+
+ @Override
+ @GraphTransaction
+ public AtlasRelationship create(AtlasRelationship relationship) throws AtlasBaseException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> create({})", relationship);
+ }
+
+ validateRelationship(relationship);
+
+ String relationshipLabel = relationship.getRelationshipLabel();
+ AtlasVertex end1Vertex = getVertexFromEndPoint(relationship.getEnd1());
+ AtlasVertex end2Vertex = getVertexFromEndPoint(relationship.getEnd2());
+
+ AtlasRelationship ret;
+
+ // create relationship between two vertex
+ try {
+ AtlasEdge relationshipEdge = getRelationshipEdge(end1Vertex, end2Vertex, relationshipLabel);
+
+ if (relationshipEdge == null) {
+ relationshipEdge = createRelationEdge(end1Vertex, end2Vertex, relationship);
+
+ AtlasRelationshipType relationType = typeRegistry.getRelationshipTypeByName(relationship.getTypeName());
+
+ if (MapUtils.isNotEmpty(relationType.getAllAttributes())) {
+ for (AtlasAttribute attr : relationType.getAllAttributes().values()) {
+ String attrName = attr.getName();
+ Object attrValue = relationship.getAttribute(attrName);
+
+ AtlasGraphUtilsV1.setProperty(relationshipEdge, attr.getVertexPropertyName(), attrValue);
+ }
+ }
+
+ // create legacy edges if mentioned in relationDef
+ createLegacyEdges(relationType.getRelationshipDef(), end1Vertex, end2Vertex);
+
+ ret = mapEdgeToAtlasRelationship(relationshipEdge);
+
+ } else {
+ throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIP_ALREADY_EXISTS, relationship.getTypeName(),
+ relationship.getEnd1().getGuid(), relationship.getEnd2().getGuid());
+ }
+ } catch (RepositoryException e) {
+ throw new AtlasBaseException(AtlasErrorCode.INTERNAL_ERROR, e);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== create({}): {}", relationship, ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ @GraphTransaction
+ public AtlasRelationship update(AtlasRelationship relationship) throws AtlasBaseException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> update({})", relationship);
+ }
+
+ AtlasRelationship ret = null;
+
+ // TODO: update(relationship) implementation
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== update({}): {}", relationship, ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ @GraphTransaction
+ public AtlasRelationship getById(String guid) throws AtlasBaseException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> getById({})", guid);
+ }
+
+ AtlasRelationship ret;
+
+ try {
+ AtlasEdge edge = graphHelper.getEdgeForGUID(guid);
+
+ ret = mapEdgeToAtlasRelationship(edge);
+ } catch (EntityNotFoundException ex) {
+ throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIP_GUID_NOT_FOUND, guid);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== getById({}): {}", guid, ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ @GraphTransaction
+ public void deleteById(String guid) throws AtlasBaseException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> deleteById({})", guid);
+ }
+
+ // TODO: deleteById(guid) implementation
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== deleteById({}): {}", guid);
+ }
+ }
+
+ private void validateRelationship(AtlasRelationship relationship) throws AtlasBaseException {
+ if (relationship == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "AtlasRelationship is null");
+ }
+
+ String relationshipName = relationship.getTypeName();
+ AtlasRelationshipType relationshipType = typeRegistry.getRelationshipTypeByName(relationshipName);
+
+ if (relationshipType == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE, "unknown relationship '" + relationshipName + "'");
+ }
+
+ AtlasObjectId end1 = relationship.getEnd1();
+
+ if (end1 == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "end1 is null");
+ }
+
+ String end1TypeName = end1.getTypeName();
+
+ if (StringUtils.isBlank(end1TypeName)) {
+ end1TypeName = AtlasGraphUtilsV1.getTypeNameFromGuid(end1.getGuid());
+ }
+
+ if (!relationshipType.getEnd1Type().isTypeOrSuperTypeOf(end1TypeName)) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_RELATIONSHIP_END_TYPE, relationshipName,
+ relationshipType.getEnd1Type().getTypeName(), end1TypeName);
+ }
+
+ AtlasObjectId end2 = relationship.getEnd2();
+
+ if (end2 == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "end2 is null");
+ }
+
+ String end2TypeName = end2.getTypeName();
+
+ if (StringUtils.isBlank(end2TypeName)) {
+ end2TypeName = AtlasGraphUtilsV1.getTypeNameFromGuid(end2.getGuid());
+ }
+
+ if (!relationshipType.getEnd2Type().isTypeOrSuperTypeOf(end2TypeName)) {
+ throw new AtlasBaseException(AtlasErrorCode.INVALID_RELATIONSHIP_END_TYPE, relationshipName,
+ relationshipType.getEnd2Type().getTypeName(), end2TypeName);
+ }
+
+ validateEnd(end1);
+
+ validateEnd(end2);
+ }
+
+ private void validateEnd(AtlasObjectId end) throws AtlasBaseException {
+ String guid = end.getGuid();
+ String typeName = end.getTypeName();
+ Map<String, Object> uniqueAttributes = end.getUniqueAttributes();
+ AtlasVertex endVertex = AtlasGraphUtilsV1.findByGuid(guid);
+
+ if (!AtlasTypeUtil.isValidGuid(guid) || endVertex == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
+ } else if (MapUtils.isNotEmpty(uniqueAttributes)) {
+ AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);
+
+ if (AtlasGraphUtilsV1.findByUniqueAttributes(entityType, uniqueAttributes) == null) {
+ throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, uniqueAttributes.toString());
+ }
+ }
+ }
+
+ private AtlasEdge getRelationshipEdge(AtlasVertex fromVertex, AtlasVertex toVertex, String relationshipLabel) {
+ AtlasEdge ret = graphHelper.getEdgeForLabel(fromVertex, relationshipLabel);
+
+ if (ret != null) {
+ AtlasVertex inVertex = ret.getInVertex();
+
+ if (inVertex != null) {
+ if (!StringUtils.equals(AtlasGraphUtilsV1.getIdFromVertex(inVertex),
+ AtlasGraphUtilsV1.getIdFromVertex(toVertex))) {
+ ret = null;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ private int getRelationVersion(AtlasRelationship relationship) {
+ Long ret = relationship != null ? relationship.getVersion() : null;
+
+ return (ret != null) ? ret.intValue() : 0;
+ }
+
+ private AtlasVertex getVertexFromEndPoint(AtlasObjectId endPoint) {
+ AtlasVertex ret = null;
+
+ if (StringUtils.isNotEmpty(endPoint.getGuid())) {
+ ret = AtlasGraphUtilsV1.findByGuid(endPoint.getGuid());
+
+ } else if (StringUtils.isNotEmpty(endPoint.getTypeName()) && MapUtils.isNotEmpty(endPoint.getUniqueAttributes())) {
+ AtlasEntityType entityType = typeRegistry.getEntityTypeByName(endPoint.getTypeName());
+
+ ret = AtlasGraphUtilsV1.findByUniqueAttributes(entityType, endPoint.getUniqueAttributes());
+ }
+
+ return ret;
+ }
+
+ private void createLegacyEdges(AtlasRelationshipDef relationshipDef, AtlasVertex fromVertex, AtlasVertex toVertex) throws RepositoryException {
+ if (relationshipDef != null) {
+ AtlasRelationshipEndDef endDef1 = relationshipDef.getEndDef1();
+ AtlasRelationshipEndDef endDef2 = relationshipDef.getEndDef2();
+
+ if (endDef1 != null && endDef1.hasLegacyRelation()) {
+ graphHelper.getOrCreateEdge(fromVertex, toVertex, endDef1.getLegacyLabel());
+ }
+
+ if (endDef2 != null && endDef2.hasLegacyRelation()) {
+ graphHelper.getOrCreateEdge(toVertex, fromVertex, endDef2.getLegacyLabel());
+ }
+ }
+ }
+
+ private AtlasEdge createRelationEdge(AtlasVertex fromVertex, AtlasVertex toVertex, AtlasRelationship relationship) throws RepositoryException {
+ AtlasEdge ret = graphHelper.getOrCreateEdge(fromVertex, toVertex, relationship.getRelationshipLabel());
+
+ // add additional properties to edge
+ if (ret != null) {
+ final String guid = UUID.randomUUID().toString();
+
+ AtlasGraphUtilsV1.setProperty(ret, Constants.ENTITY_TYPE_PROPERTY_KEY, relationship.getTypeName());
+ AtlasGraphUtilsV1.setProperty(ret, Constants.GUID_PROPERTY_KEY, guid);
+ AtlasGraphUtilsV1.setProperty(ret, Constants.VERSION_PROPERTY_KEY, getRelationVersion(relationship));
+ }
+
+ return ret;
+ }
+
+ private AtlasRelationship mapEdgeToAtlasRelationship(AtlasEdge edge) throws AtlasBaseException {
+ AtlasRelationship ret = new AtlasRelationship();
+
+ mapSystemAttributes(edge, ret);
+
+ mapAttributes(edge, ret);
+
+ return ret;
+ }
+
+ private AtlasRelationship mapSystemAttributes(AtlasEdge edge, AtlasRelationship relationship) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Mapping system attributes for relationship");
+ }
+
+ relationship.setGuid(GraphHelper.getGuid(edge));
+ relationship.setTypeName(GraphHelper.getTypeName(edge));
+
+ relationship.setCreatedBy(GraphHelper.getCreatedByAsString(edge));
+ relationship.setUpdatedBy(GraphHelper.getModifiedByAsString(edge));
+
+ relationship.setCreateTime(new Date(GraphHelper.getCreatedTime(edge)));
+ relationship.setUpdateTime(new Date(GraphHelper.getModifiedTime(edge)));
+
+ relationship.setVersion(GraphHelper.getVersion(edge).longValue());
+ relationship.setStatus(GraphHelper.getEdgeStatus(edge));
+
+ AtlasVertex end1Vertex = edge.getOutVertex();
+ AtlasVertex end2Vertex = edge.getInVertex();
+
+ relationship.setEnd1(new AtlasObjectId(GraphHelper.getGuid(end1Vertex), GraphHelper.getTypeName(end1Vertex)));
+ relationship.setEnd2(new AtlasObjectId(GraphHelper.getGuid(end2Vertex), GraphHelper.getTypeName(end2Vertex)));
+
+ return relationship;
+ }
+
+ private void mapAttributes(AtlasEdge edge, AtlasRelationship relationship) throws AtlasBaseException {
+ AtlasType objType = typeRegistry.getType(relationship.getTypeName());
+
+ if (!(objType instanceof AtlasRelationshipType)) {
+ throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_INVALID, relationship.getTypeName());
+ }
+
+ AtlasRelationshipType relationshipType = (AtlasRelationshipType) objType;
+
+ for (AtlasAttribute attribute : relationshipType.getAllAttributes().values()) {
+ // mapping only primitive attributes
+ Object attrValue = entityRetriever.mapVertexToPrimitive(edge, attribute.getQualifiedName(),
+ attribute.getAttributeDef());
+
+ relationship.setAttribute(attribute.getName(), attrValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/EntityGraphRetriever.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/EntityGraphRetriever.java
index 66f20da..9a8695a 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/EntityGraphRetriever.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/EntityGraphRetriever.java
@@ -34,6 +34,7 @@
import org.apache.atlas.repository.graph.GraphHelper;
import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasEdgeDirection;
+import org.apache.atlas.repository.graphdb.AtlasElement;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.type.*;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
@@ -460,7 +461,7 @@
return ret;
}
- private Object mapVertexToPrimitive(AtlasVertex entityVertex, final String vertexPropertyName, AtlasAttributeDef attrDef) {
+ public Object mapVertexToPrimitive(AtlasElement entityVertex, final String vertexPropertyName, AtlasAttributeDef attrDef) {
Object ret = null;
if (GraphHelper.getSingleValuedProperty(entityVertex, vertexPropertyName, Object.class) == null) {
diff --git a/repository/src/main/java/org/apache/atlas/repository/typestore/GraphBackedTypeStore.java b/repository/src/main/java/org/apache/atlas/repository/typestore/GraphBackedTypeStore.java
index 7a064b6..2dd339c 100644
--- a/repository/src/main/java/org/apache/atlas/repository/typestore/GraphBackedTypeStore.java
+++ b/repository/src/main/java/org/apache/atlas/repository/typestore/GraphBackedTypeStore.java
@@ -273,6 +273,10 @@
traits.add(new HierarchicalTypeDefinition(TraitType.class, typeName, typeDescription, superTypes, attributes));
break;
+ case RELATIONSHIP:
+ // v1 typesystem is not notified on new relation type
+ break;
+
default:
throw new IllegalArgumentException("Unhandled type category " + typeCategory);
}
diff --git a/repository/src/test/java/org/apache/atlas/TestModules.java b/repository/src/test/java/org/apache/atlas/TestModules.java
index 095af41..d28956d 100644
--- a/repository/src/test/java/org/apache/atlas/TestModules.java
+++ b/repository/src/test/java/org/apache/atlas/TestModules.java
@@ -47,9 +47,11 @@
import org.apache.atlas.repository.impexp.ExportService;
import org.apache.atlas.repository.store.graph.AtlasEntityDefStore;
import org.apache.atlas.repository.store.graph.AtlasEntityStore;
+import org.apache.atlas.repository.store.graph.AtlasRelationshipStore;
import org.apache.atlas.repository.store.graph.v1.AtlasEntityChangeNotifier;
import org.apache.atlas.repository.store.graph.v1.AtlasEntityDefStoreV1;
import org.apache.atlas.repository.store.graph.v1.AtlasEntityStoreV1;
+import org.apache.atlas.repository.store.graph.v1.AtlasRelationshipStoreV1;
import org.apache.atlas.repository.store.graph.v1.AtlasTypeDefGraphStoreV1;
import org.apache.atlas.repository.store.graph.v1.DeleteHandlerV1;
import org.apache.atlas.repository.store.graph.v1.EntityGraphMapper;
@@ -152,6 +154,7 @@
typeDefChangeListenerMultibinder.addBinding().to(GraphBackedSearchIndexer.class).asEagerSingleton();
bind(AtlasEntityStore.class).to(AtlasEntityStoreV1.class);
+ bind(AtlasRelationshipStore.class).to(AtlasRelationshipStoreV1.class);
// bind the MetadataService interface to an implementation
bind(MetadataService.class).to(DefaultMetadataService.class).asEagerSingleton();
diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java
new file mode 100644
index 0000000..144080a
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.atlas.web.rest;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasRelationship;
+import org.apache.atlas.repository.store.graph.AtlasRelationshipStore;
+import org.apache.atlas.utils.AtlasPerfTracer;
+import org.apache.atlas.web.util.Servlets;
+import org.slf4j.Logger;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+/**
+ * REST interface for entity relationships.
+ */
+@Path("v2/relation")
+@Singleton
+@Service
+public class RelationshipREST {
+ private static final Logger PERF_LOG = AtlasPerfTracer.getPerfLogger("rest.RelationshipREST");
+
+ private final AtlasRelationshipStore relationshipStore;
+
+ @Inject
+ public RelationshipREST(AtlasRelationshipStore relationshipStore) {
+ this.relationshipStore = relationshipStore;
+ }
+
+ /**
+ * Create a new relationship between entities.
+ */
+ @POST
+ @Consumes(Servlets.JSON_MEDIA_TYPE)
+ @Produces(Servlets.JSON_MEDIA_TYPE)
+ public AtlasRelationship create(AtlasRelationship relationship) throws AtlasBaseException {
+ AtlasPerfTracer perf = null;
+
+ try {
+ if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+ perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "RelationshipREST.create(" + relationship + ")");
+ }
+
+ return relationshipStore.create(relationship);
+ } finally {
+ AtlasPerfTracer.log(perf);
+ }
+ }
+
+ /**
+ * Update an existing relationship between entities.
+ */
+ @PUT
+ @Consumes(Servlets.JSON_MEDIA_TYPE)
+ @Produces(Servlets.JSON_MEDIA_TYPE)
+ public AtlasRelationship update(AtlasRelationship relationship) throws AtlasBaseException {
+ AtlasPerfTracer perf = null;
+
+ try {
+ if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+ perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "RelationshipREST.update(" + relationship + ")");
+ }
+
+ return relationshipStore.update(relationship);
+ } finally {
+ AtlasPerfTracer.log(perf);
+ }
+ }
+
+ /**
+ * Get relationship information between entities using guid.
+ */
+ @GET
+ @Path("/guid/{guid}")
+ @Consumes(Servlets.JSON_MEDIA_TYPE)
+ @Produces(Servlets.JSON_MEDIA_TYPE)
+ public AtlasRelationship getById(@PathParam("guid") String guid) throws AtlasBaseException {
+ AtlasPerfTracer perf = null;
+
+ try {
+ if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+ perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "RelationshipREST.getById(" + guid + ")");
+ }
+
+ return relationshipStore.getById(guid);
+ } finally {
+ AtlasPerfTracer.log(perf);
+ }
+ }
+
+ /**
+ * Delete a relationship between entities using guid.
+ */
+ @DELETE
+ @Path("/guid/{guid}")
+ @Consumes(Servlets.JSON_MEDIA_TYPE)
+ @Produces(Servlets.JSON_MEDIA_TYPE)
+ public void deleteById(@PathParam("guid") String guid) throws AtlasBaseException {
+ AtlasPerfTracer perf = null;
+
+ try {
+ if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+ perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "RelationshipREST.deleteById(" + guid + ")");
+ }
+
+ relationshipStore.deleteById(guid);
+ } finally {
+ AtlasPerfTracer.log(perf);
+ }
+ }
+}