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);
+        }
+    }
+}