atlas-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yhema...@apache.org
Subject [1/5] incubator-atlas git commit: ATLAS-491 Business Catalog / Taxonomy (jspeidel via yhemanth)
Date Wed, 18 May 2016 13:03:57 GMT
Repository: incubator-atlas
Updated Branches:
  refs/heads/master b65dd91c3 -> aaf2971ad


http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/query/AlwaysQueryExpressionTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/query/AlwaysQueryExpressionTest.java b/catalog/src/test/java/org/apache/atlas/catalog/query/AlwaysQueryExpressionTest.java
new file mode 100644
index 0000000..81d70a4
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/query/AlwaysQueryExpressionTest.java
@@ -0,0 +1,74 @@
+/**
+ * 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.catalog.query;
+
+import org.apache.atlas.catalog.VertexWrapper;
+import org.testng.annotations.Test;
+
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Unit tests for AlwaysQueryExpression.
+ */
+public class AlwaysQueryExpressionTest {
+    @Test
+    public void testEvaluate() {
+        VertexWrapper v = createStrictMock(VertexWrapper.class);
+        replay(v);
+        QueryExpression expression = new AlwaysQueryExpression();
+        // always returns true
+        assertTrue(expression.evaluate(v));
+        verify(v);
+    }
+
+    @Test
+    public void testEvaluate_negated() {
+        VertexWrapper v = createStrictMock(VertexWrapper.class);
+        replay(v);
+        QueryExpression expression = new AlwaysQueryExpression();
+        expression.setNegate();
+        // always returns true
+        assertFalse(expression.evaluate(v));
+        assertTrue(expression.isNegate());
+        verify(v);
+    }
+
+    @Test
+    public void testGetProperties() {
+        VertexWrapper v = createStrictMock(VertexWrapper.class);
+        replay(v);
+        QueryExpression expression = new AlwaysQueryExpression();
+        assertTrue(expression.getProperties().isEmpty());
+        verify(v);
+    }
+
+    @Test
+    public void testAsPipe() {
+        VertexWrapper v = createStrictMock(VertexWrapper.class);
+        replay(v);
+        QueryExpression expression = new AlwaysQueryExpression();
+        assertNull(expression.asPipe());
+        verify(v);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/query/QueryFactoryTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/query/QueryFactoryTest.java b/catalog/src/test/java/org/apache/atlas/catalog/query/QueryFactoryTest.java
new file mode 100644
index 0000000..36cb6dc
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/query/QueryFactoryTest.java
@@ -0,0 +1,209 @@
+/**
+ * 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.catalog.query;
+
+import org.apache.atlas.catalog.CollectionRequest;
+import org.apache.atlas.catalog.InstanceRequest;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.TermPath;
+import org.apache.atlas.catalog.definition.EntityResourceDefinition;
+import org.apache.atlas.catalog.definition.EntityTagResourceDefinition;
+import org.testng.annotations.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Unit tests for QueryFactory.
+ */
+public class QueryFactoryTest {
+    @Test
+    public void testCreateTaxonomyQuery() throws Exception {
+        Map<String, Object> requestProps = new HashMap<>();
+        requestProps.put("name", "test_taxonomy");
+        Request request = new InstanceRequest(requestProps);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), TermQueryExpression.class);
+        assertEquals(queryExpression.getField(), "name");
+        assertEquals(queryExpression.getExpectedValue(), "test_taxonomy");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+
+    @Test
+    public void testCreateTermQuery() throws Exception {
+        Map<String, Object> requestProps = new HashMap<>();
+        requestProps.put("name", "test_taxonomy.term1");
+        requestProps.put("termPath", new TermPath("test_taxonomy.term1"));
+        Request request = new InstanceRequest(requestProps);
+
+
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTermQuery query = (AtlasTermQuery) factory.createTermQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), TermQueryExpression.class);
+        assertEquals(queryExpression.getField(), "name");
+        assertEquals(queryExpression.getExpectedValue(), "test_taxonomy.term1");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Term");
+    }
+
+    @Test
+    public void testCreateEntityQuery() throws Exception {
+        Map<String, Object> requestProps = new HashMap<>();
+        requestProps.put("id", "foo");
+        Request request = new InstanceRequest(requestProps);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasEntityQuery query = (AtlasEntityQuery) factory.createEntityQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), TermQueryExpression.class);
+        assertEquals(queryExpression.getField(), "id");
+        assertEquals(queryExpression.getExpectedValue(), "foo");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getClass(), EntityResourceDefinition.class);
+    }
+
+    @Test
+    public void testCreateEntityTagQuery() throws Exception {
+        Map<String, Object> requestProps = new HashMap<>();
+        requestProps.put("id", "entity_id");
+        requestProps.put("name", "test_taxonomy.term1");
+        Request request = new InstanceRequest(requestProps);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasEntityTagQuery query = (AtlasEntityTagQuery) factory.createEntityTagQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), TermQueryExpression.class);
+        assertEquals(queryExpression.getField(), "name");
+        assertEquals(queryExpression.getExpectedValue(), "test_taxonomy.term1");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getClass(), EntityTagResourceDefinition.class);
+    }
+
+    @Test
+    public void testCollectionQuery_TermQuery() throws Exception {
+        String queryString = "name:test_taxonomy";
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), TermQueryExpression.class);
+        assertEquals(queryExpression.getField(), "name");
+        assertEquals(queryExpression.getExpectedValue(), "test_taxonomy");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+
+    @Test
+    public void testCollectionQuery_PrefixQuery() throws Exception {
+        String queryString = "name:t*";
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), PrefixQueryExpression.class);
+        assertEquals(queryExpression.getField(), "name");
+        assertEquals(queryExpression.getExpectedValue(), "t");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+
+    @Test
+    public void testCollectionQuery_TermRangeQuery() throws Exception {
+        String queryString = "creation_time:[2013-01-01:07:29:00 TO 2017-01-02]";
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), TermRangeQueryExpression.class);
+        assertEquals(queryExpression.getField(), "creation_time");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+
+    @Test
+    public void testCollectionQuery_WildcardQuery() throws Exception {
+        String queryString = "name:ta?onomy";
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), WildcardQueryExpression.class);
+        assertEquals(queryExpression.getField(), "name");
+        assertEquals(queryExpression.getExpectedValue(), "ta?onomy");
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+
+    @Test
+    public void testCollectionQuery_BooleanQuery() throws Exception {
+        String queryString = "name:foo OR name:bar";
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), BooleanQueryExpression.class);
+
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+
+    @Test
+    public void testCollectionQuery_ProjectionQuery() throws Exception {
+        String queryString = "relation/name:foo";
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+
+        QueryFactory factory = new QueryFactory();
+        AtlasTaxonomyQuery query = (AtlasTaxonomyQuery) factory.createTaxonomyQuery(request);
+
+        QueryExpression queryExpression = query.getQueryExpression();
+        assertEquals(queryExpression.getClass(), ProjectionQueryExpression.class);
+
+        ProjectionQueryExpression projectionExpression = (ProjectionQueryExpression) queryExpression;
+        QueryExpression underlyingExpression = projectionExpression.getUnderlyingExpression();
+        assertEquals(underlyingExpression.getClass(), TermQueryExpression.class);
+        assertEquals(underlyingExpression.getField(), QueryFactory.escape("relation/name"));
+        assertEquals(underlyingExpression.getExpectedValue(), "foo");
+
+        assertEquals(query.getRequest(), request);
+        assertEquals(query.getResourceDefinition().getTypeName(), "Taxonomy");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/distro/src/conf/policy-store.txt
----------------------------------------------------------------------
diff --git a/distro/src/conf/policy-store.txt b/distro/src/conf/policy-store.txt
index b072b49..5bcc1cf 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:*
-typeReadPolicy;;nixon:rw;;;;type:*,entity:*
-classReadPolicy;;saqeeb:r;;;;type:*,entity:*
-dataScientistPolicy;;;;DATA_SCIENTIST:r;;type:*,entity:*
-dataStewardPolicy;;;;DATA_STEWARD:rwu;;type:*,entity:*
+adminPolicy;;admin:rwud;;ROLE_ADMIN:rwud;;type:*,entity:*,operation:*,taxonomy:*,term:*
+typeReadPolicy;;nixon:rw;;;;type:*,entity:*,taxonomy:*,term:*
+classReadPolicy;;saqeeb:r;;;;type:*,entity:*,taxonomy:*,term:*
+dataScientistPolicy;;;;DATA_SCIENTIST:r;;type:*,entity:*,taxonomy:*,term:*
+dataStewardPolicy;;;;DATA_STEWARD:rwu;;type:*,entity:*,taxonomy:*,term:*

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5e2871e..30fb95a 100755
--- a/pom.xml
+++ b/pom.xml
@@ -463,6 +463,7 @@
         <module>graphdb</module>
         <module>titan</module>
         <module>repository</module>
+        <module>catalog</module>
         <!-- <module>dashboard</module> -->
         <module>dashboardv2</module>
         <module>webapp</module>
@@ -1087,6 +1088,12 @@
                 <version>${project.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.apache.atlas</groupId>
+                <artifactId>atlas-catalog</artifactId>
+                <version>${project.version}</version>
+            </dependency>         
+
             <!--Scala dependencies-->
             <dependency>
                 <groupId>org.scala-lang</groupId>
@@ -1238,7 +1245,7 @@
             <dependency>
                 <groupId>org.easymock</groupId>
                 <artifactId>easymock</artifactId>
-                <version>2.4</version>
+                <version>3.4</version>
                 <scope>test</scope>
             </dependency>
 

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index a68010a..d7e182d 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -21,6 +21,7 @@ ATLAS-409 Atlas will not import avro tables with schema read from a file (dosset
 ATLAS-379 Create sqoop and falcon metadata addons (venkatnrangan,bvellanki,sowmyaramesh via shwethags)
 
 ALL CHANGES:
+ATLAS-491 Business Catalog / Taxonomy (jspeidel via yhemanth)
 ATLAS-713 Entity lineage based on entity id (shwethags)
 ATLAS-736 UI - BUG :: displaying timestamp values for hive_db description (kevalbhatt18 via yhemanth)
 ATLAS-784 Configure config.store.uri for Falcon hook IT (yhemanth)

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/repository/src/main/java/org/apache/atlas/services/DefaultMetadataService.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/services/DefaultMetadataService.java b/repository/src/main/java/org/apache/atlas/services/DefaultMetadataService.java
index 5195cbe..7cd83f8 100755
--- a/repository/src/main/java/org/apache/atlas/services/DefaultMetadataService.java
+++ b/repository/src/main/java/org/apache/atlas/services/DefaultMetadataService.java
@@ -313,10 +313,14 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
 
         ITypedReferenceableInstance[] typedInstances = deserializeClassInstances(entityInstanceDefinition);
 
-        final List<String> guids = repository.createEntities(typedInstances);
+        List<String> guids = createEntities(typedInstances);
+        return new JSONArray(guids).toString();
+    }
 
+    public List<String> createEntities(ITypedReferenceableInstance[] typedInstances) throws AtlasException {
+        final List<String> guids = repository.createEntities(typedInstances);
         onEntitiesAdded(guids);
-        return new JSONArray(guids).toString();
+        return guids;
     }
 
     private ITypedReferenceableInstance[] deserializeClassInstances(String entityInstanceDefinition)
@@ -327,18 +331,7 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
             for (int index = 0; index < referableInstances.length(); index++) {
                 Referenceable entityInstance =
                         InstanceSerialization.fromJsonReferenceable(referableInstances.getString(index), true);
-                final String entityTypeName = entityInstance.getTypeName();
-                ParamChecker.notEmpty(entityTypeName, "Entity type cannot be null");
-
-                ClassType entityType = typeSystem.getDataType(ClassType.class, entityTypeName);
-
-                //Both assigned id and values are required for full update
-                //classtype.convert() will remove values if id is assigned. So, set temp id, convert and
-                // then replace with original id
-                Id origId = entityInstance.getId();
-                entityInstance.replaceWithNewId(new Id(entityInstance.getTypeName()));
-                ITypedReferenceableInstance typedInstrance = entityType.convert(entityInstance, Multiplicity.REQUIRED);
-                ((ReferenceableInstance)typedInstrance).replaceWithNewId(origId);
+                ITypedReferenceableInstance typedInstrance = getTypedReferenceableInstance(entityInstance);
                 instances[index] = typedInstrance;
             }
             return instances;
@@ -350,6 +343,23 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
         }
     }
 
+    @Override
+    public ITypedReferenceableInstance getTypedReferenceableInstance(Referenceable entityInstance) throws AtlasException {
+        final String entityTypeName = entityInstance.getTypeName();
+        ParamChecker.notEmpty(entityTypeName, "Entity type cannot be null");
+
+        ClassType entityType = typeSystem.getDataType(ClassType.class, entityTypeName);
+
+        //Both assigned id and values are required for full update
+        //classtype.convert() will remove values if id is assigned. So, set temp id, convert and
+        // then replace with original id
+        Id origId = entityInstance.getId();
+        entityInstance.replaceWithNewId(new Id(entityInstance.getTypeName()));
+        ITypedReferenceableInstance typedInstrance = entityType.convert(entityInstance, Multiplicity.REQUIRED);
+        ((ReferenceableInstance)typedInstrance).replaceWithNewId(origId);
+        return typedInstrance;
+    }
+
     /**
      * Return the definition for the given guid.
      *
@@ -579,6 +589,10 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
         ParamChecker.notEmpty(traitInstanceDefinition, "trait instance definition");
 
         ITypedStruct traitInstance = deserializeTraitInstance(traitInstanceDefinition);
+        addTrait(guid, traitInstance);
+    }
+
+    public void addTrait(String guid, ITypedStruct traitInstance) throws AtlasException {
         final String traitName = traitInstance.getTypeName();
 
         // ensure trait type is already registered with the TS
@@ -591,7 +605,7 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
         // ensure trait is not already defined
         Preconditions
             .checkArgument(!getTraitNames(guid).contains(traitName), "trait=%s is already defined for entity=%s",
-                traitName, guid);
+                    traitName, guid);
 
         repository.addTrait(guid, traitInstance);
 
@@ -601,8 +615,12 @@ public class DefaultMetadataService implements MetadataService, ActiveStateChang
     private ITypedStruct deserializeTraitInstance(String traitInstanceDefinition)
     throws AtlasException {
 
+        return createTraitInstance(InstanceSerialization.fromJsonStruct(traitInstanceDefinition, true));
+    }
+
+    @Override
+    public ITypedStruct createTraitInstance(Struct traitInstance) throws AtlasException {
         try {
-            Struct traitInstance = InstanceSerialization.fromJsonStruct(traitInstanceDefinition, true);
             final String entityTypeName = traitInstance.getTypeName();
             ParamChecker.notEmpty(entityTypeName, "entity type");
 

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/server-api/src/main/java/org/apache/atlas/services/MetadataService.java
----------------------------------------------------------------------
diff --git a/server-api/src/main/java/org/apache/atlas/services/MetadataService.java b/server-api/src/main/java/org/apache/atlas/services/MetadataService.java
index 13d20d8..c8c1067 100644
--- a/server-api/src/main/java/org/apache/atlas/services/MetadataService.java
+++ b/server-api/src/main/java/org/apache/atlas/services/MetadataService.java
@@ -21,7 +21,10 @@ package org.apache.atlas.services;
 import org.apache.atlas.AtlasException;
 import org.apache.atlas.EntityAuditEvent;
 import org.apache.atlas.listener.EntityChangeListener;
+import org.apache.atlas.typesystem.ITypedReferenceableInstance;
+import org.apache.atlas.typesystem.ITypedStruct;
 import org.apache.atlas.typesystem.Referenceable;
+import org.apache.atlas.typesystem.Struct;
 import org.apache.atlas.typesystem.types.DataTypes;
 import org.codehaus.jettison.json.JSONObject;
 
@@ -80,6 +83,26 @@ public interface MetadataService {
     String createEntities(String entityDefinition) throws AtlasException;
 
     /**
+     * Get a typed entity instance.
+     *
+     * @param entity entity
+     * @return typed entity instance
+     *
+     * @throws AtlasException if any failure occurs
+     */
+    ITypedReferenceableInstance getTypedReferenceableInstance(Referenceable entity) throws AtlasException;
+
+    /**
+     * Create entity instances.
+     *
+     * @param typedInstances  instance to create
+     * @return collection of guids for created entities
+     *
+     * @throws AtlasException if unable to create the entities
+     */
+    List<String> createEntities(ITypedReferenceableInstance[] typedInstances) throws AtlasException;
+
+    /**
      * Return the definition for the given guid.
      *
      * @param guid guid
@@ -166,6 +189,11 @@ public interface MetadataService {
      */
     void addTrait(String guid, String traitInstanceDefinition) throws AtlasException;
 
+    //todo:
+    void addTrait(String guid, ITypedStruct traitInstance) throws AtlasException;
+    ITypedStruct createTraitInstance(Struct traitInstance) throws AtlasException;
+
+
     /**
      * Deletes a given trait from an existing entity represented by a guid.
      *

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/pom.xml
----------------------------------------------------------------------
diff --git a/webapp/pom.xml b/webapp/pom.xml
index de48c15..4b67ffa 100755
--- a/webapp/pom.xml
+++ b/webapp/pom.xml
@@ -108,6 +108,11 @@
             <groupId>org.apache.hadoop</groupId>
             <artifactId>hadoop-hdfs</artifactId>
         </dependency>
+   
+        <dependency>
+            <groupId>org.apache.atlas</groupId>
+            <artifactId>atlas-catalog</artifactId>
+        </dependency>
 
         <!-- supports simple auth handler -->
         <dependency>

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
index 211ee7f..14a2aac 100644
--- a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
+++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
@@ -19,8 +19,6 @@
 package org.apache.atlas.authorize;
 
 import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
 import java.util.List;
 
 import javax.servlet.ServletException;
@@ -55,9 +53,13 @@ public class AtlasAuthorizationUtils {
         }
         if (u.startsWith(BASE_URL)) {
             u = parse(u, BASE_URL);
+        } else {
+            // strip of leading '/'
+            u = u.substring(1);
         }
         String[] split = u.split("/");
-        return split[0];
+        String api = split[0];
+        return (! api.equals("v1")) ? api : String.format("v1/%s", split[1]);
     }
 
     public static AtlasActionTypes getAtlasAction(String method) {
@@ -99,7 +101,7 @@ public class AtlasAuthorizationUtils {
 
         if (api.startsWith("types")) {
             resourceTypes.add(AtlasResourceTypes.TYPE);
-        } else if ((api.startsWith("discovery") && api.contains("gremlin")) || api.startsWith("admin")
+        } else if ((api.startsWith("discovery") && contextPath.contains("gremlin")) || api.startsWith("admin")
             || api.startsWith("graph")) {
             resourceTypes.add(AtlasResourceTypes.OPERATION);
         } else if ((api.startsWith("entities") && contextPath.contains("traits")) || api.startsWith("discovery")) {
@@ -107,6 +109,21 @@ public class AtlasAuthorizationUtils {
             resourceTypes.add(AtlasResourceTypes.TYPE);
         } else if (api.startsWith("entities") || api.startsWith("lineage")) {
             resourceTypes.add(AtlasResourceTypes.ENTITY);
+        } else if (api.startsWith("v1/taxonomies")) {
+            resourceTypes.add(AtlasResourceTypes.TAXONOMY);
+            // taxonomies are modeled as entities
+            resourceTypes.add(AtlasResourceTypes.ENTITY);
+            if (contextPath.contains("terms")) {
+                resourceTypes.add(AtlasResourceTypes.TERM);
+                // terms are modeled as traits
+                resourceTypes.add(AtlasResourceTypes.TYPE);
+            }
+        } else if (api.startsWith("v1/entities")) {
+            resourceTypes.add(AtlasResourceTypes.ENTITY);
+            if (contextPath.contains("tags")) {
+                // tags are modeled as traits
+                resourceTypes.add(AtlasResourceTypes.TYPE);
+            }
         } else {
             LOG.error("Unable to find Atlas Resource corresponding to : " + api);
             throw new ServletException("Unable to find Atlas Resource corresponding to : " + api);

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
index 8ce3f4c..14a72f1 100644
--- a/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
+++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java
@@ -19,5 +19,5 @@
 package org.apache.atlas.authorize;
 
 public enum AtlasResourceTypes {
-    ENTITY, TYPE, OPERATION;
+    ENTITY, TYPE, OPERATION, TAXONOMY, TERM;
 }

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/web/resources/BaseService.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/BaseService.java b/webapp/src/main/java/org/apache/atlas/web/resources/BaseService.java
new file mode 100644
index 0000000..3982df8
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/BaseService.java
@@ -0,0 +1,126 @@
+/**
+ * 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.resources;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import org.apache.atlas.catalog.*;
+import org.apache.atlas.catalog.exception.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Base class for all v1 API services.
+ */
+public abstract class BaseService {
+    private static final Gson gson = new Gson();
+    private final Logger LOG = LoggerFactory.getLogger(getClass());
+
+    protected Result getResource(ResourceProvider provider, Request request)
+            throws ResourceNotFoundException {
+
+        try {
+            return provider.getResourceById(request);
+        } catch (RuntimeException e) {
+            throw wrapRuntimeException(e);
+        }
+    }
+
+    protected Result getResources(ResourceProvider provider, Request request)
+            throws ResourceNotFoundException, InvalidQueryException {
+
+        try {
+            return provider.getResources(request);
+        } catch (RuntimeException e) {
+            throw wrapRuntimeException(e);
+        }
+    }
+
+    protected void createResource(ResourceProvider provider, Request request) throws CatalogException {
+        try {
+            provider.createResource(request);
+        } catch (RuntimeException e) {
+            throw wrapRuntimeException(e);
+        }
+    }
+
+    protected Collection<String> createResources(ResourceProvider provider, Request request) throws CatalogException {
+
+        try {
+            return provider.createResources(request);
+        } catch (RuntimeException e) {
+            throw wrapRuntimeException(e);
+        }
+    }
+
+    protected String getQueryString(@Context UriInfo ui) {
+        String uri = ui.getRequestUri().toASCIIString();
+        int qsBegin = uri.indexOf("?");
+        return (qsBegin == -1) ? null : uri.substring(qsBegin + 1);
+    }
+
+    protected <T extends Map> T parsePayload(String body) throws InvalidPayloadException {
+        T properties;
+
+        try {
+            properties = gson.<T>fromJson(body, Map.class);
+        } catch (JsonSyntaxException e) {
+            LOG.info("Unable to parse json in request body", e);
+            throw new InvalidPayloadException("Request payload contains invalid JSON: " + e.getMessage());
+        }
+
+        return properties;
+    }
+
+    private RuntimeException wrapRuntimeException(RuntimeException e) {
+        return e instanceof CatalogRuntimeException ? e : new CatalogRuntimeException(e);
+    }
+
+    protected String decode(String s) throws CatalogException {
+        try {
+            return s == null ? null : URLDecoder.decode(s, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new CatalogException("Unable to decode URL: " + e.getMessage(), 500);
+        }
+    }
+
+    @XmlRootElement
+    // the name of this class is used as the collection name in the returned json when returning a collection
+    public static class Results {
+        public String href;
+        public int status;
+
+        public Results() {
+            // required by JAXB
+        }
+
+        public Results(String href, int status) {
+            this.href = href;
+            this.status = status;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/web/resources/CatalogExceptionMapper.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/CatalogExceptionMapper.java b/webapp/src/main/java/org/apache/atlas/web/resources/CatalogExceptionMapper.java
new file mode 100644
index 0000000..4f7da2e
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/CatalogExceptionMapper.java
@@ -0,0 +1,62 @@
+/**
+ * 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.web.resources;
+
+import org.apache.atlas.catalog.exception.CatalogException;
+import org.apache.atlas.web.util.Servlets;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Exception mapper for CatalogException.
+ */
+@Provider
+public class CatalogExceptionMapper implements ExceptionMapper<CatalogException> {
+    @Override
+    public Response toResponse(CatalogException e) {
+        return Response.status(e.getStatus()).entity(
+                new ErrorBean(e)).type(Servlets.JSON_MEDIA_TYPE).build();
+    }
+
+    @XmlRootElement
+    public static class ErrorBean {
+        public int status;
+        public String message;
+
+        public ErrorBean() {
+            // required for JAXB
+        }
+
+        public ErrorBean(CatalogException ex) {
+            this.status = ex.getStatus();
+            this.message = ex.getMessage();
+        }
+
+        public int getStatus() {
+            return status;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/web/resources/CatalogRuntimeExceptionMapper.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/CatalogRuntimeExceptionMapper.java b/webapp/src/main/java/org/apache/atlas/web/resources/CatalogRuntimeExceptionMapper.java
new file mode 100644
index 0000000..e11d6d1
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/CatalogRuntimeExceptionMapper.java
@@ -0,0 +1,78 @@
+/**
+ * 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.web.resources;
+
+import org.apache.atlas.catalog.exception.CatalogRuntimeException;
+import org.apache.atlas.web.util.Servlets;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Exception mapper for CatalogRuntimeException
+ */
+@Provider
+public class CatalogRuntimeExceptionMapper implements ExceptionMapper<CatalogRuntimeException> {
+    @Override
+    public Response toResponse(CatalogRuntimeException e) {
+        return Response.status(e.getStatusCode()).entity(
+                new ErrorBean(e)).type(Servlets.JSON_MEDIA_TYPE).build();
+    }
+
+    @XmlRootElement
+    public static class ErrorBean {
+        private static final String MSG_PREFIX = "An unexpected error has occurred. ";
+        public int status;
+        public String message;
+        public String stackTrace;
+        //todo: error code, developerMsg ...
+
+        public ErrorBean() {
+            // required for JAXB
+        }
+
+        public ErrorBean(CatalogRuntimeException ex) {
+            this.status = 500;
+            this.message = String.format("%s%s : %s", MSG_PREFIX, ex.toString(), ex.getCause().toString());
+            this.stackTrace = getStackTraceFromException(ex);
+        }
+
+        public int getStatus() {
+            return status;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public String getStackTrace() {
+            return stackTrace;
+        }
+
+        private String getStackTraceFromException(RuntimeException e) {
+            StringWriter sw = new StringWriter();
+            e.printStackTrace(new PrintWriter(sw));
+            return sw.toString();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/web/resources/EntityService.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/EntityService.java b/webapp/src/main/java/org/apache/atlas/web/resources/EntityService.java
new file mode 100644
index 0000000..0e3f4c4
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/EntityService.java
@@ -0,0 +1,147 @@
+/**
+ * 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.resources;
+
+import org.apache.atlas.catalog.*;
+import org.apache.atlas.catalog.exception.CatalogException;
+import org.apache.atlas.services.MetadataService;
+import org.apache.atlas.web.util.Servlets;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.*;
+import javax.ws.rs.core.*;
+import java.util.*;
+
+/**
+ * Service which handles API requests for v1 entity resources.
+ */
+@Path("v1/entities")
+@Singleton
+public class EntityService extends BaseService {
+
+    private final EntityResourceProvider entityResourceProvider;
+    private final EntityTagResourceProvider entityTagResourceProvider;
+    private static JsonSerializer m_serializer = new JsonSerializer();
+
+    @Inject
+    public EntityService(MetadataService metadataService) {
+        DefaultTypeSystem typeSystem = new DefaultTypeSystem(metadataService);
+        entityResourceProvider = new EntityResourceProvider(typeSystem);
+        entityTagResourceProvider = new EntityTagResourceProvider(typeSystem);
+    }
+
+    @GET
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getEntities(@Context HttpHeaders headers, @Context UriInfo ui) throws CatalogException {
+        String queryString = decode(getQueryString(ui));
+
+        BaseRequest request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+        Result result = getResources(entityResourceProvider, request);
+
+        return Response.status(Response.Status.OK).entity(m_serializer.serialize(result, ui)).build();
+    }
+
+    @GET
+    @Path("{entityId}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getEntity(@Context HttpHeaders headers,
+                              @Context UriInfo ui,
+                              @PathParam("entityId") String entityId) throws CatalogException {
+
+        BaseRequest request = new InstanceRequest(Collections.<String, Object>singletonMap("id", entityId));
+        Result result = getResource(entityResourceProvider, request);
+
+        return Response.status(Response.Status.OK).entity(m_serializer.serialize(result, ui)).build();
+    }
+
+    @GET
+    @Path("{entityId}/tags/{tag}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getEntityTag(@Context HttpHeaders headers,
+                                 @Context UriInfo ui,
+                                 @PathParam("entityId") String entityId,
+                                 @PathParam("tag") String tagName) throws CatalogException {
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("id", entityId);
+        properties.put("name", tagName);
+        Result result = getResource(entityTagResourceProvider, new InstanceRequest(properties));
+
+        return Response.status(Response.Status.OK).entity(m_serializer.serialize(result, ui)).build();
+    }
+
+    @GET
+    @Path("{entityId}/tags")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getEntityTags(@Context HttpHeaders headers,
+                                  @Context UriInfo ui,
+                                  @PathParam("entityId") String entityGuid) throws CatalogException {
+
+        BaseRequest request = new CollectionRequest(Collections.<String, Object>singletonMap("id", entityGuid),
+                decode(getQueryString(ui)));
+        Result result = getResources(entityTagResourceProvider, request);
+
+        return Response.status(Response.Status.OK).entity(m_serializer.serialize(result, ui)).build();
+    }
+
+    @POST
+    @Path("{entityId}/tags/{tag}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response tagEntity(String body,
+                              @Context HttpHeaders headers,
+                              @Context UriInfo ui,
+                              @PathParam("entityId") String entityId,
+                              @PathParam("tag") String tagName) throws CatalogException {
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("id", entityId);
+        properties.put("name", tagName);
+        createResource(entityTagResourceProvider, new InstanceRequest(properties));
+
+        return Response.status(Response.Status.CREATED).entity(
+                new Results(ui.getRequestUri().toString(), 201)).build();
+    }
+
+    @POST
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response tagEntities(String body,
+                                @Context HttpHeaders headers,
+                                @Context UriInfo ui) throws CatalogException {
+
+        Map<String, Object> properties = parsePayload(body);
+
+        if (properties.get("tags") == null || properties.size() != 1) {
+            throw new CatalogException(
+                    "Invalid Request, no 'tags' property specified. Creation of entity resource not supported.", 400);
+
+        }
+        String queryString = decode(getQueryString(ui));
+        Collection<String> createResults = createResources(
+                entityTagResourceProvider, new CollectionRequest(properties, queryString));
+
+        Collection<Results> result = new ArrayList<>();
+        for (String relativeUrl : createResults) {
+            result.add(new Results(ui.getBaseUri().toString() + relativeUrl, 201));
+        }
+
+        return Response.status(Response.Status.CREATED).entity(
+                new GenericEntity<Collection<Results>>(result) {}).build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/java/org/apache/atlas/web/resources/TaxonomyService.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/TaxonomyService.java b/webapp/src/main/java/org/apache/atlas/web/resources/TaxonomyService.java
new file mode 100644
index 0000000..ccd0b62
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/TaxonomyService.java
@@ -0,0 +1,201 @@
+/**
+ * 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.resources;
+
+import org.apache.atlas.catalog.*;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.exception.CatalogException;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.services.MetadataService;
+import org.apache.atlas.web.util.Servlets;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.*;
+import javax.ws.rs.core.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Service which handles API requests for taxonomy and term resources.
+ */
+@Path("v1/taxonomies")
+@Singleton
+public class TaxonomyService extends BaseService {
+
+    private final TaxonomyResourceProvider taxonomyResourceProvider;
+    private static TermResourceProvider termResourceProvider;
+    private static JsonSerializer serializer = new JsonSerializer();
+
+    @Inject
+    public TaxonomyService(MetadataService metadataService) {
+        DefaultTypeSystem typeSystem = new DefaultTypeSystem(metadataService);
+        taxonomyResourceProvider = new TaxonomyResourceProvider(typeSystem);
+        termResourceProvider = new TermResourceProvider(typeSystem);
+    }
+
+    @GET
+    @Path("{taxonomyName}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getTaxonomy(@Context HttpHeaders headers,
+                                @Context UriInfo ui,
+                                @PathParam("taxonomyName") String taxonomyName) throws CatalogException {
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("name", taxonomyName);
+        Result result = getResource(taxonomyResourceProvider, new InstanceRequest(properties));
+        return Response.status(Response.Status.OK).entity(serializer.serialize(result, ui)).build();
+    }
+
+    @GET
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getTaxonomies(@Context HttpHeaders headers, @Context UriInfo ui) throws CatalogException {
+        String queryString = decode(getQueryString(ui));
+        Request request = new CollectionRequest(Collections.<String, Object>emptyMap(), queryString);
+        Result result = getResources(taxonomyResourceProvider, request);
+        return Response.status(Response.Status.OK).entity(serializer.serialize(result, ui)).build();
+    }
+
+    @POST
+    @Path("{taxonomyName}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response createTaxonomy(String body,
+                                   @Context HttpHeaders headers,
+                                   @Context UriInfo ui,
+                                   @PathParam("taxonomyName") String taxonomyName) throws CatalogException {
+
+        Map<String, Object> properties = parsePayload(body);
+        properties.put("name", taxonomyName);
+
+        createResource(taxonomyResourceProvider, new InstanceRequest(properties));
+
+        return Response.status(Response.Status.CREATED).entity(
+                new Results(ui.getRequestUri().toString(), 201)).build();
+    }
+
+    @GET
+    @Path("{taxonomyName}/terms/{termName}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getTaxonomyTerm(@Context HttpHeaders headers,
+                                    @Context UriInfo ui,
+                                    @PathParam("taxonomyName") String taxonomyName,
+                                    @PathParam("termName") String termName) throws CatalogException {
+
+        TermPath termPath = new TermPath(taxonomyName, termName);
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("termPath", termPath);
+        Result result = getResource(termResourceProvider, new InstanceRequest(properties));
+
+        return Response.status(Response.Status.OK).entity(serializer.serialize(result, ui)).build();
+    }
+
+    @GET
+    @Path("{taxonomyName}/terms")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getTaxonomyTerms(@Context HttpHeaders headers,
+                                     @Context UriInfo ui,
+                                     @PathParam("taxonomyName") String taxonomyName) throws CatalogException {
+
+        String queryString = decode(getQueryString(ui));
+        TermPath termPath = new TermPath(taxonomyName, null);
+        Request request = new CollectionRequest(
+                Collections.<String, Object>singletonMap("termPath", termPath), queryString);
+        Result result = getResources(termResourceProvider, request);
+
+        return Response.status(Response.Status.OK).entity(serializer.serialize(result, ui)).build();
+    }
+
+    @GET
+    @Path("{taxonomyName}/terms/{rootTerm}/{remainder:.*}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response getSubTerms(@Context HttpHeaders headers,
+                            @Context UriInfo ui,
+                            @PathParam("taxonomyName") String taxonomyName,
+                            @PathParam("rootTerm") String rootTerm,
+                            @PathParam("remainder") String remainder) throws CatalogException {
+
+        Result result;
+
+        List<PathSegment> pathSegments = ui.getPathSegments();
+
+        int lastIndex = pathSegments.size() - 1;
+        String lastSegment = pathSegments.get(lastIndex).getPath();
+        String termName = String.format("%s%s", rootTerm,
+                remainder.replaceAll("/?terms/?([.]*)", "$1."));
+        String queryString = decode(getQueryString(ui));
+        TermPath termPath = new TermPath(taxonomyName, termName);
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("termPath", termPath);
+        if (lastSegment.equals("terms") || (lastSegment.isEmpty() && pathSegments.get(lastIndex - 1).getPath().equals("terms"))) {
+            result = getResources(termResourceProvider, new CollectionRequest(properties, queryString));
+        } else {
+            result = getResource(termResourceProvider, new InstanceRequest(properties));
+        }
+
+        return Response.status(Response.Status.OK).entity(serializer.serialize(result, ui)).build();
+    }
+
+    @POST
+    @Path("{taxonomyName}/terms/{termName}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response createTerm(String body,
+                               @Context HttpHeaders headers,
+                               @Context UriInfo ui,
+                               @PathParam("taxonomyName") String taxonomyName,
+                               @PathParam("termName") String termName) throws CatalogException {
+
+        Map<String, Object> properties = parsePayload(body);
+        validateName(termName);
+        properties.put("termPath", new TermPath(taxonomyName, termName));
+        createResource(termResourceProvider, new InstanceRequest(properties));
+
+        return Response.status(Response.Status.CREATED).entity(
+                new Results(ui.getRequestUri().toString(), 201)).build();
+    }
+
+    @POST
+    @Path("{taxonomyName}/terms/{termName}/{remainder:.*}")
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public Response createSubTerms(String body,
+                                   @Context HttpHeaders headers,
+                                   @Context UriInfo ui,
+                                   @PathParam("taxonomyName") String taxonomyName,
+                                   @PathParam("termName") String termName,
+                                   @PathParam("remainder") String remainder) throws CatalogException {
+
+        Map<String, Object> properties = parsePayload(body);
+        String[] pathTokens = remainder.split("/");
+        validateName(pathTokens[pathTokens.length -1]);
+        properties.put("termPath", new TermPath(taxonomyName, String.format("%s%s", termName,
+                remainder.replaceAll("/?terms/?([.]*)", "$1."))));
+        createResource(termResourceProvider, new InstanceRequest(properties));
+
+        return Response.status(Response.Status.CREATED).entity(
+                new Results(ui.getRequestUri().toString(), 201)).build();
+    }
+
+    private void validateName(String name) throws InvalidPayloadException {
+        if (name.contains(".")) {
+            throw new InvalidPayloadException("The \"name\" property may not contain the character '.'");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/webapp/src/main/webapp/WEB-INF/web.xml b/webapp/src/main/webapp/WEB-INF/web.xml
index f0b606e..34b6856 100755
--- a/webapp/src/main/webapp/WEB-INF/web.xml
+++ b/webapp/src/main/webapp/WEB-INF/web.xml
@@ -30,6 +30,11 @@
             org.apache.atlas.web.resources,org.apache.atlas.web.params
         </param-value>
     </context-param>
+  
+    <context-param>
+        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
+        <param-value>true</param-value>
+    </context-param>
 
     <!--
         More information can be found here:

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/webapp/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsTest.java
----------------------------------------------------------------------
diff --git a/webapp/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsTest.java b/webapp/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsTest.java
new file mode 100644
index 0000000..5fc4420
--- /dev/null
+++ b/webapp/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsTest.java
@@ -0,0 +1,121 @@
+/**
+ * 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.authorize;
+
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Unit tests for AtlasAuthorizationUtils.
+ */
+public class AtlasAuthorizationUtilsTest {
+    @Test
+    public void testGetApi() {
+        String contextPath = "/api/atlas/entities";
+        assertEquals(AtlasAuthorizationUtils.getApi(contextPath), "entities");
+
+        contextPath = "/api/atlas/entities/111/traits";
+        assertEquals(AtlasAuthorizationUtils.getApi(contextPath), "entities");
+
+        contextPath = "/api/atlas/v1/entities";
+        assertEquals(AtlasAuthorizationUtils.getApi(contextPath), "v1/entities");
+
+        contextPath = "/api/atlas/v1/entities/111/tags";
+        assertEquals(AtlasAuthorizationUtils.getApi(contextPath), "v1/entities");
+
+        // not sure of this use case but the code appears to support url's that don't
+        // begin with base url.
+        contextPath = "/foo/bar";
+        assertEquals(AtlasAuthorizationUtils.getApi(contextPath), "foo");
+    }
+
+    @Test
+    public void testGetAtlasResourceType() throws Exception {
+        String contextPath = "/api/atlas/types";
+        List<AtlasResourceTypes> resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TYPE));
+
+        contextPath = "/api/atlas/admin/foo";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.OPERATION));
+
+        contextPath = "/api/atlas/graph/foo";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.OPERATION));
+
+        contextPath = "/api/atlas/discovery/search/gremlin";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.OPERATION));
+
+        contextPath = "/api/atlas/entities/111/traits";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 2);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TYPE));
+
+        contextPath = "/api/atlas/discovery/search";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 2);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TYPE));
+
+        contextPath = "/api/atlas/entities?type=Column";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+
+        contextPath = "/api/atlas/lineage";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+
+        contextPath = "/api/atlas/v1/taxonomies";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 2);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TAXONOMY));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+
+        contextPath = "/api/atlas/v1/taxonomies/taxonomy1/terms";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 4);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TAXONOMY));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TERM));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TYPE));
+
+        contextPath = "/api/atlas/v1/entities/111";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 1);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+
+        contextPath = "/api/atlas/v1/entities/111/tags/foo";
+        resourceTypes = AtlasAuthorizationUtils.getAtlasResourceType(contextPath);
+        assertEquals(resourceTypes.size(), 2);
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.ENTITY));
+        assertTrue(resourceTypes.contains(AtlasResourceTypes.TYPE));
+    }
+}


Mime
View raw message