ATLAS-1892: update AtlasEntityType to populate relationship attributes during resolve

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 925b6b1..9902685 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, RELATION
+    UNKNOWN, ENTITY, TYPE, OPERATION, TAXONOMY, TERM, RELATIONSHIP
 }
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 bb3157a..d67376f 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
@@ -1,4 +1,5 @@
 /**
+/**
  * 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
@@ -31,7 +32,6 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.HashSet;
-import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
 
@@ -139,8 +139,8 @@
             if (contextPath.contains("/terms")) {
                 resourceTypes.add(AtlasResourceTypes.TERM);
             }
-        } else if (api.startsWith("relation")) {
-            resourceTypes.add(AtlasResourceTypes.RELATION);
+        } else if (api.startsWith("relationship")) {
+            resourceTypes.add(AtlasResourceTypes.RELATIONSHIP);
         } 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 acf7388..fc611c8 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,8 +231,8 @@
                     resourceType = AtlasResourceTypes.TAXONOMY;
                 } else if (type.equalsIgnoreCase("TERM")) {
                     resourceType = AtlasResourceTypes.TERM;
-                } else if (type.equalsIgnoreCase("RELATION")) {
-                    resourceType = AtlasResourceTypes.RELATION;
+                } else if (type.equalsIgnoreCase("RELATIONSHIP")) {
+                    resourceType = AtlasResourceTypes.RELATIONSHIP;
                 } else {
                     Log.warn(type + " is invalid resource please check PolicyStore file");
                     continue;
diff --git a/distro/src/conf/policy-store.txt b/distro/src/conf/policy-store.txt
index 47583c1..c804b8d 100644
--- a/distro/src/conf/policy-store.txt
+++ b/distro/src/conf/policy-store.txt
@@ -2,8 +2,8 @@
 ##r-READ, w-WRITE, u-UPDATE, d-DELETE
 ##Policy_Name;;User_Name1:Operations_Allowed,User_Name2:Operations_Allowed;;Group_Name1:Operations_Allowed,Group_Name2:Operations_Allowed;;Resource_Type1:Resource_Name,Resource_Type2:Resource_Name
 ##
-adminPolicy;;admin:rwud;;ROLE_ADMIN:rwud;;type:*,entity:*,operation:*,taxonomy:*,term:*
-dataScientistPolicy;;;;DATA_SCIENTIST:r;;type:*,entity:*,taxonomy:*,term:*
-dataStewardPolicy;;;;DATA_STEWARD:rwu;;type:*,entity:*,taxonomy:*,term:*
-hadoopPolicy;;;;hadoop:rwud;;type:*,entity:*,operation:*,taxonomy:*,term:*
+adminPolicy;;admin:rwud;;ROLE_ADMIN:rwud;;type:*,entity:*,operation:*,taxonomy:*,term:*,relationship:*
+dataScientistPolicy;;;;DATA_SCIENTIST:r;;type:*,entity:*,taxonomy:*,term:*,relationship:*
+dataStewardPolicy;;;;DATA_STEWARD:rwu;;type:*,entity:*,taxonomy:*,term:*,relationship:*
+hadoopPolicy;;;;hadoop:rwud;;type:*,entity:*,operation:*,taxonomy:*,term:*,relationship:*
 rangerTagSyncPolicy;;;;RANGER_TAG_SYNC:r;;type:*,entity:*
diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index e8971a8..70480df 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -93,6 +93,7 @@
     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"),
+    RELATIONSHIP_CRUD_INVALID_PARAMS(404, "ATLAS-404-00-00D", "Invalid relationship creation/updation parameters passed : {0}"),
 
      // All data conflict errors go here
     TYPE_ALREADY_EXISTS(409, "ATLAS-409-00-001", "Given type {0} already exists"),
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 34e932e..f80ea89 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
@@ -166,10 +166,6 @@
         return this.cardinality;
     }
 
-    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; }
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 a29f7fb..70e3067 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
@@ -47,10 +47,11 @@
 
     private final AtlasEntityDef entityDef;
 
-    private List<AtlasEntityType> superTypes         = Collections.emptyList();
-    private Set<String>           allSuperTypes      = Collections.emptySet();
-    private Set<String>           allSubTypes        = Collections.emptySet();
-    private Set<String>           typeAndAllSubTypes = Collections.emptySet();
+    private List<AtlasEntityType>       superTypes             = Collections.emptyList();
+    private Set<String>                 allSuperTypes          = Collections.emptySet();
+    private Set<String>                 allSubTypes            = Collections.emptySet();
+    private Set<String>                 typeAndAllSubTypes     = Collections.emptySet();
+    private Map<String, AtlasAttribute> relationshipAttributes = Collections.emptyMap();
 
     public AtlasEntityType(AtlasEntityDef entityDef) {
         super(entityDef);
@@ -88,12 +89,13 @@
             }
         }
 
-        this.superTypes         = Collections.unmodifiableList(s);
-        this.allSuperTypes      = Collections.unmodifiableSet(allS);
-        this.allAttributes      = Collections.unmodifiableMap(allA);
-        this.uniqAttributes     = getUniqueAttributes(this.allAttributes);
-        this.allSubTypes        = new HashSet<>();   // this will be populated in resolveReferencesPhase2()
-        this.typeAndAllSubTypes = new HashSet<>();   // this will be populated in resolveReferencesPhase2()
+        this.superTypes             = Collections.unmodifiableList(s);
+        this.allSuperTypes          = Collections.unmodifiableSet(allS);
+        this.allAttributes          = Collections.unmodifiableMap(allA);
+        this.uniqAttributes         = getUniqueAttributes(this.allAttributes);
+        this.allSubTypes            = new HashSet<>();   // this will be populated in resolveReferencesPhase2()
+        this.typeAndAllSubTypes     = new HashSet<>();   // this will be populated in resolveReferencesPhase2()
+        this.relationshipAttributes = new HashMap<>();   // this will be populated in resolveReferencesPhase2()
 
         this.typeAndAllSubTypes.add(this.getTypeName());
     }
@@ -140,6 +142,12 @@
         return StringUtils.isNotEmpty(entityTypeName) && allSuperTypes.contains(entityTypeName);
     }
 
+    public Map<String, AtlasAttribute> getRelationshipAttributes() { return Collections.unmodifiableMap(relationshipAttributes); }
+
+    public void addRelationshipAttribute(String attributeName, AtlasAttribute attribute) {
+        relationshipAttributes.put(attributeName, attribute);
+    }
+
     @Override
     public AtlasEntity createDefaultValue() {
         AtlasEntity ret = new AtlasEntity(entityDef.getName());
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 38b68bb..f85cf35 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasRelationshipType.java
@@ -20,14 +20,16 @@
 
 import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
 import org.apache.atlas.model.typedef.AtlasRelationshipDef;
 import org.apache.atlas.model.typedef.AtlasRelationshipDef.RelationshipCategory;
 import org.apache.atlas.model.typedef.AtlasRelationshipEndDef;
 import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef;
+import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef.Cardinality;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 /**
  * class that implements behaviour of an relationship-type.
  */
@@ -57,20 +59,26 @@
     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;
+        if (relationshipDef == null) {
+            throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE, "relationshipDef is null");
+        }
+
+        String end1TypeName = relationshipDef.getEndDef1() != null ? relationshipDef.getEndDef1().getType() : null;
+        String end2TypeName = relationshipDef.getEndDef2() != null ? relationshipDef.getEndDef2().getType() : null;
 
         AtlasType type1 = typeRegistry.getType(end1TypeName);
         AtlasType type2 = typeRegistry.getType(end2TypeName);
 
         if (type1 instanceof AtlasEntityType) {
-            end1Type = (AtlasEntityType)type1;
+            end1Type = (AtlasEntityType) type1;
+
         } else {
             throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIPDEF_INVALID_END_TYPE, getTypeName(), end1TypeName);
         }
 
         if (type2 instanceof AtlasEntityType) {
-            end2Type = (AtlasEntityType)type2;
+            end2Type = (AtlasEntityType) type2;
+
         } else {
             throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIPDEF_INVALID_END_TYPE, getTypeName(), end2TypeName);
         }
@@ -79,12 +87,25 @@
     }
 
     @Override
+    public void resolveReferencesPhase2(AtlasTypeRegistry typeRegistry) throws AtlasBaseException {
+        super.resolveReferencesPhase2(typeRegistry);
+
+        addRelationshipAttributeToEndType(relationshipDef.getEndDef1(), end1Type, end2Type.getTypeName(), typeRegistry);
+
+        addRelationshipAttributeToEndType(relationshipDef.getEndDef2(), end2Type, end1Type.getTypeName(), typeRegistry);
+    }
+
+    @Override
     public boolean isValidValue(Object obj) {
         boolean ret = true;
 
         if (obj != null) {
-           validateAtlasRelationshipType((AtlasRelationshipType) obj);
-           ret = super.isValidValue(obj);
+
+            if (obj instanceof AtlasRelationshipType) {
+                validateAtlasRelationshipType((AtlasRelationshipType) obj);
+            }
+
+            ret = super.isValidValue(obj);
         }
 
         return ret;
@@ -176,4 +197,29 @@
             }
         }
     }
+
+    private void addRelationshipAttributeToEndType(AtlasRelationshipEndDef endDef,
+                                                   AtlasEntityType entityType,
+                                                   String attrTypeName,
+                                                   AtlasTypeRegistry typeRegistry) throws AtlasBaseException {
+
+        String attrName = (endDef != null) ? endDef.getName() : null;
+
+        if (StringUtils.isEmpty(attrName)) {
+            return;
+        }
+
+        AtlasAttribute attribute = entityType.getAttribute(attrName);
+
+        if (attribute == null) { //attr doesn't exist in type - is a new relationship attribute
+
+            if (endDef.getCardinality() == Cardinality.SET) {
+                attrTypeName = AtlasBaseTypeDef.getArrayTypeName(attrTypeName);
+            }
+
+            attribute = new AtlasAttribute(entityType, new AtlasAttributeDef(attrName, attrTypeName), typeRegistry.getType(attrTypeName));
+        }
+
+        entityType.addRelationshipAttribute(attrName, attribute);
+    }
 }
diff --git a/intg/src/test/java/org/apache/atlas/type/TestAtlasRelationshipType.java b/intg/src/test/java/org/apache/atlas/type/TestAtlasRelationshipType.java
index 7a4e9fd..41aac8f 100644
--- a/intg/src/test/java/org/apache/atlas/type/TestAtlasRelationshipType.java
+++ b/intg/src/test/java/org/apache/atlas/type/TestAtlasRelationshipType.java
@@ -17,15 +17,48 @@
  */
 package org.apache.atlas.type;
 
+import com.google.common.collect.ImmutableSet;
 import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
+import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
+import org.apache.atlas.type.AtlasTypeRegistry.AtlasTransientTypeRegistry;
+import org.apache.atlas.model.typedef.AtlasEntityDef;
 import org.apache.atlas.model.typedef.AtlasRelationshipDef;
+import org.apache.atlas.model.typedef.AtlasRelationshipDef.PropagateTags;
+import org.apache.atlas.model.typedef.AtlasRelationshipDef.RelationshipCategory;
 import org.apache.atlas.model.typedef.AtlasRelationshipEndDef;
 import org.apache.atlas.model.typedef.AtlasStructDef;
+import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef.Cardinality;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
 import static org.testng.Assert.fail;
+
 public class TestAtlasRelationshipType {
+
+    private AtlasTypeRegistry typeRegistry = new AtlasTypeRegistry();
+
+    private static final String EMPLOYEE_TYPE                  = "employee";
+    private static final String DEPARTMENT_TYPE                = "department";
+    private static final String ADDRESS_TYPE                   = "address";
+    private static final String PHONE_TYPE                     = "phone";
+    private static final String DEPT_EMPLOYEE_RELATION_TYPE    = "departmentEmployee";
+    private static final String EMPLOYEE_ADDRESS_RELATION_TYPE = "employeeAddress";
+    private static final String EMPLOYEE_PHONE_RELATION_TYPE   = "employeePhone";
+
+    @Test
+    public void createTypesAndRelationships() throws AtlasBaseException {
+        createEmployeeTypes();
+
+        createRelationshipTypes();
+    }
+
     @Test
     public void testvalidateAtlasRelationshipDef() throws AtlasBaseException {
         AtlasRelationshipEndDef ep1 = new AtlasRelationshipEndDef("typeA", "attr1", AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE);
@@ -104,4 +137,119 @@
         }
 
     }
+
+    @Test(dependsOnMethods = "createTypesAndRelationships")
+    public void testRelationshipAttributes() throws Exception {
+        Map<String, AtlasAttribute> employeeRelationAttrs = getRelationAttrsForType(EMPLOYEE_TYPE);
+
+        Assert.assertNotNull(employeeRelationAttrs);
+        Assert.assertEquals(employeeRelationAttrs.size(), 2);
+
+        Assert.assertTrue(employeeRelationAttrs.containsKey("department"));
+        Assert.assertTrue(employeeRelationAttrs.containsKey("address"));
+
+        AtlasAttribute deptAttr = employeeRelationAttrs.get("department");
+        Assert.assertEquals(deptAttr.getTypeName(), DEPARTMENT_TYPE);
+
+        AtlasAttribute addrAttr = employeeRelationAttrs.get("address");
+        Assert.assertEquals(addrAttr.getTypeName(), ADDRESS_TYPE);
+
+        Map<String, AtlasAttribute> deptRelationAttrs = getRelationAttrsForType(DEPARTMENT_TYPE);
+
+        Assert.assertNotNull(deptRelationAttrs);
+        Assert.assertEquals(deptRelationAttrs.size(), 1);
+        Assert.assertTrue(deptRelationAttrs.containsKey("employees"));
+
+        AtlasAttribute employeesAttr = deptRelationAttrs.get("employees");
+        Assert.assertEquals(employeesAttr.getTypeName(),AtlasBaseTypeDef.getArrayTypeName(EMPLOYEE_TYPE));
+
+        Map<String, AtlasAttribute> addressRelationAttrs = getRelationAttrsForType(ADDRESS_TYPE);
+
+        Assert.assertNotNull(addressRelationAttrs);
+        Assert.assertEquals(addressRelationAttrs.size(), 1);
+        Assert.assertTrue(addressRelationAttrs.containsKey("employees"));
+
+        AtlasAttribute employeesAttr1 = addressRelationAttrs.get("employees");
+        Assert.assertEquals(employeesAttr1.getTypeName(),AtlasBaseTypeDef.getArrayTypeName(EMPLOYEE_TYPE));
+    }
+
+    @Test(dependsOnMethods = "testRelationshipAttributes")
+    public void testRelationshipAttributesOnExistingAttributes() throws Exception {
+        AtlasRelationshipDef employeePhoneRelationDef = new AtlasRelationshipDef(EMPLOYEE_PHONE_RELATION_TYPE, getDescription(EMPLOYEE_PHONE_RELATION_TYPE), "1.0",
+                                                                                 RelationshipCategory.ASSOCIATION, PropagateTags.ONE_TO_TWO,
+                                                                                 new AtlasRelationshipEndDef(EMPLOYEE_TYPE, "phone_no", Cardinality.SINGLE),
+                                                                                 new AtlasRelationshipEndDef(PHONE_TYPE, "owner", Cardinality.SINGLE));
+
+        createType(employeePhoneRelationDef);
+
+        Map<String, AtlasAttribute> employeeRelationshipAttrs = getRelationAttrsForType(EMPLOYEE_TYPE);
+        Map<String, AtlasAttribute> employeeAttrs             = getAttrsForType(EMPLOYEE_TYPE);
+
+        // validate if phone_no exists in both relationAttributes and attributes
+        Assert.assertTrue(employeeRelationshipAttrs.containsKey("phone_no"));
+        Assert.assertTrue(employeeAttrs.containsKey("phone_no"));
+    }
+
+    private void createEmployeeTypes() throws AtlasBaseException {
+        AtlasEntityDef phoneDef      = AtlasTypeUtil.createClassTypeDef(PHONE_TYPE, getDescription(PHONE_TYPE), ImmutableSet.<String>of(),
+                                                                        AtlasTypeUtil.createRequiredAttrDef("phone_number", "int"),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("area_code", "int"),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("owner", EMPLOYEE_TYPE));
+
+        AtlasEntityDef employeeDef   = AtlasTypeUtil.createClassTypeDef(EMPLOYEE_TYPE, getDescription(EMPLOYEE_TYPE), ImmutableSet.<String>of(),
+                                                                        AtlasTypeUtil.createRequiredAttrDef("name", "string"),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("dob", "date"),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("age", "int"),
+                                                                        AtlasTypeUtil.createRequiredAttrDef("phone_no", PHONE_TYPE));
+
+        AtlasEntityDef departmentDef = AtlasTypeUtil.createClassTypeDef(DEPARTMENT_TYPE, getDescription(DEPARTMENT_TYPE), ImmutableSet.<String>of(),
+                                                                        AtlasTypeUtil.createRequiredAttrDef("name", "string"),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("count", "int"));
+
+        AtlasEntityDef addressDef    = AtlasTypeUtil.createClassTypeDef(ADDRESS_TYPE, getDescription(ADDRESS_TYPE), ImmutableSet.<String>of(),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("street", "string"),
+                                                                        AtlasTypeUtil.createRequiredAttrDef("city", "string"),
+                                                                        AtlasTypeUtil.createRequiredAttrDef("state", "string"),
+                                                                        AtlasTypeUtil.createOptionalAttrDef("zip", "int"));
+
+        createTypes(new ArrayList<>(Arrays.asList(phoneDef, employeeDef, departmentDef, addressDef)));
+    }
+
+    private void createRelationshipTypes() throws AtlasBaseException {
+        AtlasRelationshipDef deptEmployeeRelationDef = new AtlasRelationshipDef(DEPT_EMPLOYEE_RELATION_TYPE, getDescription(DEPT_EMPLOYEE_RELATION_TYPE), "1.0",
+                                                                                RelationshipCategory.ASSOCIATION, PropagateTags.ONE_TO_TWO,
+                                                                                new AtlasRelationshipEndDef(EMPLOYEE_TYPE, "department", Cardinality.SINGLE),
+                                                                                new AtlasRelationshipEndDef(DEPARTMENT_TYPE, "employees", Cardinality.SET));
+
+        AtlasRelationshipDef employeeAddrRelationDef = new AtlasRelationshipDef(EMPLOYEE_ADDRESS_RELATION_TYPE, getDescription(EMPLOYEE_ADDRESS_RELATION_TYPE), "1.0",
+                                                                                RelationshipCategory.ASSOCIATION, PropagateTags.ONE_TO_TWO,
+                                                                                new AtlasRelationshipEndDef(EMPLOYEE_TYPE, "address", Cardinality.SINGLE),
+                                                                                new AtlasRelationshipEndDef(ADDRESS_TYPE, "employees", Cardinality.SET));
+
+        createTypes(new ArrayList<>(Arrays.asList(deptEmployeeRelationDef, employeeAddrRelationDef)));
+    }
+
+    private void createType(AtlasBaseTypeDef typeDef) throws AtlasBaseException {
+        createTypes(new ArrayList<>(Arrays.asList(typeDef)));
+    }
+
+    private void createTypes(List<? extends AtlasBaseTypeDef> typeDefs) throws AtlasBaseException {
+        AtlasTransientTypeRegistry ttr = typeRegistry.lockTypeRegistryForUpdate();
+
+        ttr.addTypes(typeDefs);
+
+        typeRegistry.releaseTypeRegistryForUpdate(ttr, true);
+    }
+
+    private String getDescription(String typeName) {
+        return typeName + " description";
+    }
+
+    private Map<String, AtlasAttribute> getRelationAttrsForType(String typeName) {
+        return typeRegistry.getEntityTypeByName(typeName).getRelationshipAttributes();
+    }
+
+    private Map<String, AtlasAttribute> getAttrsForType(String typeName) {
+        return typeRegistry.getEntityTypeByName(typeName).getAllAttributes();
+    }
 }
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
index 8fe4888..8621233 100644
--- 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
@@ -20,6 +20,7 @@
 import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.annotation.GraphTransaction;
 import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.TypeCategory;
 import org.apache.atlas.model.instance.AtlasObjectId;
 import org.apache.atlas.model.instance.AtlasRelationship;
 import org.apache.atlas.model.typedef.AtlasRelationshipDef;
@@ -29,11 +30,9 @@
 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;
@@ -46,13 +45,16 @@
 import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
 @Component
 public class AtlasRelationshipStoreV1 implements AtlasRelationshipStore {
     private static final Logger LOG = LoggerFactory.getLogger(AtlasRelationshipStoreV1.class);
+    private static final int DEFAULT_RELATIONSHIP_VERSION = 0;
 
     private final AtlasTypeRegistry    typeRegistry;
     private final EntityGraphRetriever entityRetriever;
@@ -182,7 +184,7 @@
         AtlasRelationshipType relationshipType = typeRegistry.getRelationshipTypeByName(relationshipName);
 
         if (relationshipType == null) {
-            throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE, "unknown relationship '" + relationshipName + "'");
+            throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE, "unknown relationship type'" + relationshipName + "'");
         }
 
         AtlasObjectId end1 = relationship.getEnd1();
@@ -222,6 +224,8 @@
         validateEnd(end1);
 
         validateEnd(end2);
+
+        validateAndNormalize(relationship);
     }
 
     private void validateEnd(AtlasObjectId end) throws AtlasBaseException {
@@ -241,6 +245,28 @@
         }
     }
 
+    private void validateAndNormalize(AtlasRelationship relationship) throws AtlasBaseException {
+        List<String> messages = new ArrayList<>();
+
+        if (! AtlasTypeUtil.isValidGuid(relationship.getGuid())) {
+            throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIP_GUID_NOT_FOUND, relationship.getGuid());
+        }
+
+        AtlasRelationshipType type = typeRegistry.getRelationshipTypeByName(relationship.getTypeName());
+
+        if (type == null) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_INVALID, TypeCategory.RELATIONSHIP.name(), relationship.getTypeName());
+        }
+
+        type.validateValue(relationship, relationship.getTypeName(), messages);
+
+        if (!messages.isEmpty()) {
+            throw new AtlasBaseException(AtlasErrorCode.RELATIONSHIP_CRUD_INVALID_PARAMS, messages);
+        }
+
+        type.getNormalizedValue(relationship);
+    }
+
     private AtlasEdge getRelationshipEdge(AtlasVertex fromVertex, AtlasVertex toVertex, String relationshipLabel) {
         AtlasEdge ret = graphHelper.getEdgeForLabel(fromVertex, relationshipLabel);
 
@@ -261,7 +287,7 @@
     private int getRelationVersion(AtlasRelationship relationship) {
         Long ret = relationship != null ? relationship.getVersion() : null;
 
-        return (ret != null) ? ret.intValue() : 0;
+        return (ret != null) ? ret.intValue() : DEFAULT_RELATIONSHIP_VERSION;
     }
 
     private AtlasVertex getVertexFromEndPoint(AtlasObjectId endPoint) {
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
index 144080a..7174543 100644
--- a/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java
@@ -40,7 +40,7 @@
 /**
  * REST interface for entity relationships.
  */
-@Path("v2/relation")
+@Path("v2/relationship")
 @Singleton
 @Service
 public class RelationshipREST {
@@ -68,6 +68,7 @@
             }
 
             return relationshipStore.create(relationship);
+
         } finally {
             AtlasPerfTracer.log(perf);
         }
@@ -88,6 +89,7 @@
             }
 
             return relationshipStore.update(relationship);
+
         } finally {
             AtlasPerfTracer.log(perf);
         }
@@ -109,6 +111,7 @@
             }
 
             return relationshipStore.getById(guid);
+
         } finally {
             AtlasPerfTracer.log(perf);
         }
@@ -134,4 +137,4 @@
             AtlasPerfTracer.log(perf);
         }
     }
-}
+}
\ No newline at end of file