Repository: incubator-atlas Updated Branches: refs/heads/master c2d52759f -> 242b5585c
ATLAS-1856: AtlasRelationship instance implementation Java/REST Signed-off-by: Madhan Neethiraj <mad...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/incubator-atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-atlas/commit/242b5585 Tree: http://git-wip-us.apache.org/repos/asf/incubator-atlas/tree/242b5585 Diff: http://git-wip-us.apache.org/repos/asf/incubator-atlas/diff/242b5585 Branch: refs/heads/master Commit: 242b5585c97a9565ac35641d8a1ed8ee3a8fd883 Parents: c2d5275 Author: Sarath Subramanian <ssubraman...@hortonworks.com> Authored: Mon Jun 19 18:54:20 2017 -0700 Committer: Madhan Neethiraj <mad...@apache.org> Committed: Mon Jun 19 20:38:53 2017 -0700 ---------------------------------------------------------------------- .../atlas/authorize/AtlasResourceTypes.java | 2 +- .../simple/AtlasAuthorizationUtils.java | 2 + .../atlas/authorize/simple/PolicyParser.java | 2 + .../repository/graphdb/AtlasGraphQuery.java | 6 + .../titan/query/NativeTitanGraphQuery.java | 6 + .../graphdb/titan/query/TitanGraphQuery.java | 25 +- .../titan0/query/NativeTitan0GraphQuery.java | 6 + .../repository/graphdb/titan1/Titan1Graph.java | 17 +- .../titan1/query/NativeTitan1GraphQuery.java | 8 + .../java/org/apache/atlas/AtlasErrorCode.java | 4 + .../atlas/model/instance/AtlasRelationship.java | 243 ++++++++++++ .../model/typedef/AtlasRelationshipEndDef.java | 56 ++- .../org/apache/atlas/type/AtlasEntityType.java | 4 + .../atlas/type/AtlasRelationshipType.java | 25 ++ .../apache/atlas/type/AtlasTypeRegistry.java | 3 + .../atlas/repository/graph/GraphHelper.java | 64 ++-- .../store/graph/AtlasRelationshipStore.java | 53 +++ .../store/graph/v1/AtlasGraphUtilsV1.java | 13 + .../graph/v1/AtlasRelationshipStoreV1.java | 365 +++++++++++++++++++ .../store/graph/v1/EntityGraphRetriever.java | 3 +- .../typestore/GraphBackedTypeStore.java | 4 + .../test/java/org/apache/atlas/TestModules.java | 3 + .../apache/atlas/web/rest/RelationshipREST.java | 137 +++++++ 23 files changed, 1001 insertions(+), 50 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/authorization/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java ---------------------------------------------------------------------- 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 } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasAuthorizationUtils.java ---------------------------------------------------------------------- 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 @@ public class AtlasAuthorizationUtils { 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()); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/authorization/src/main/java/org/apache/atlas/authorize/simple/PolicyParser.java ---------------------------------------------------------------------- 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 @@ public class PolicyParser { 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; http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/graphdb/api/src/main/java/org/apache/atlas/repository/graphdb/AtlasGraphQuery.java ---------------------------------------------------------------------- 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 @@ public interface AtlasGraphQuery<V, E> { */ 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 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/NativeTitanGraphQuery.java ---------------------------------------------------------------------- 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 @@ package org.apache.atlas.repository.graphdb.titan.query; 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 @@ public interface NativeTitanGraphQuery<V, E> { */ Iterable<AtlasVertex<V, E>> vertices(); + /** + * Executes the graph query. + * @return + */ + Iterable<AtlasEdge<V, E>> edges(); /** * Adds an in condition to the query. http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/graphdb/common/src/main/java/org/apache/atlas/repository/graphdb/titan/query/TitanGraphQuery.java ---------------------------------------------------------------------- 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.HashSet; 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 @@ public abstract class TitanGraphQuery<V, E> implements AtlasGraphQuery<V, E> { @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 @@ public abstract class TitanGraphQuery<V, E> implements AtlasGraphQuery<V, E> { } @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)); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/graphdb/titan0/src/main/java/org/apache/atlas/repository/graphdb/titan0/query/NativeTitan0GraphQuery.java ---------------------------------------------------------------------- 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 @@ package org.apache.atlas.repository.graphdb.titan0.query; 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 @@ public class NativeTitan0GraphQuery implements NativeTitanGraphQuery<Titan0Verte 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) { http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/Titan1Graph.java ---------------------------------------------------------------------- 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 @@ public class Titan1Graph implements AtlasGraph<Titan1Vertex, Titan1Edge> { 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 class Titan1Graph implements AtlasGraph<Titan1Vertex, Titan1Edge> { } - 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 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/graphdb/titan1/src/main/java/org/apache/atlas/repository/graphdb/titan1/query/NativeTitan1GraphQuery.java ---------------------------------------------------------------------- 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 @@ package org.apache.atlas.repository.graphdb.titan1.query; 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 @@ public class NativeTitan1GraphQuery implements NativeTitanGraphQuery<Titan1Verte } @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); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java ---------------------------------------------------------------------- diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java index 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 @@ public enum AtlasErrorCode { 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 @@ public enum AtlasErrorCode { 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}"), http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/intg/src/main/java/org/apache/atlas/model/instance/AtlasRelationship.java ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java ---------------------------------------------------------------------- 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 @@ public class AtlasRelationshipEndDef implements Serializable { * 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 @@ public class AtlasRelationshipEndDef implements Serializable { * - 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 @@ public class AtlasRelationshipEndDef implements Serializable { 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 @@ public class AtlasRelationshipEndDef implements Serializable { 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 @@ public class AtlasRelationshipEndDef implements Serializable { 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 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java ---------------------------------------------------------------------- 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 @@ public class AtlasEntityType extends AtlasStructType { 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()); } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java ---------------------------------------------------------------------- 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 @@ public class AtlasRelationshipType extends AtlasStructType { 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 class AtlasRelationshipType extends AtlasStructType { 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 @@ public class AtlasRelationshipType extends AtlasStructType { 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 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/intg/src/main/java/org/apache/atlas/type/AtlasTypeRegistry.java ---------------------------------------------------------------------- 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 @@ public class AtlasTypeRegistry { 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 @@ public class AtlasTypeRegistry { 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 @@ public class AtlasTypeRegistry { addTypesWithNoRefResolve(parent.getAllStructDefs()); addTypesWithNoRefResolve(parent.getAllClassificationDefs()); addTypesWithNoRefResolve(parent.getAllEntityDefs()); + addTypesWithNoRefResolve(parent.getAllRelationshipDefs()); addedTypes.clear(); updatedTypes.clear(); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java index 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 com.google.common.collect.HashBiMap; 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 @@ public final class GraphHelper { * 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)); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Found {} with condition {}", string(element), getConditionString(args)); } - return vertex; + 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 @@ public final class GraphHelper { 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 @@ public final class GraphHelper { 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 @@ public final class GraphHelper { 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 @@ public final class GraphHelper { 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); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasRelationshipStore.java ---------------------------------------------------------------------- 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; +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasGraphUtilsV1.java ---------------------------------------------------------------------- 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; 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 @@ public class AtlasGraphUtilsV1 { 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() http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasRelationshipStoreV1.java ---------------------------------------------------------------------- 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 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/EntityGraphRetriever.java ---------------------------------------------------------------------- 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.Constants; 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 @@ public final class EntityGraphRetriever { 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) { http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/main/java/org/apache/atlas/repository/typestore/GraphBackedTypeStore.java ---------------------------------------------------------------------- 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 @@ public class GraphBackedTypeStore implements ITypeStore { 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); } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/repository/src/test/java/org/apache/atlas/TestModules.java ---------------------------------------------------------------------- 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.graphdb.AtlasGraph; 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 @@ public class TestModules { 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(); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/242b5585/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java ---------------------------------------------------------------------- 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); + } + } +}