Commit 82c06b74 by Venkatesh Seetharam

BUG-32815 BUG-32816 BUG-32827 Entity Trait Management Backend API. Contributed…

BUG-32815 BUG-32816 BUG-32827 Entity Trait Management Backend API. Contributed by Venkatesh Seetharam
parent 6ea66f7a
...@@ -33,7 +33,24 @@ public interface EntityChangeListener { ...@@ -33,7 +33,24 @@ public interface EntityChangeListener {
* @param typedInstance a typed instance * @param typedInstance a typed instance
* @throws org.apache.hadoop.metadata.MetadataException * @throws org.apache.hadoop.metadata.MetadataException
*/ */
void onAdd(String typeName, void onEntityAdded(String typeName,
ITypedReferenceableInstance typedInstance) throws MetadataException; ITypedReferenceableInstance typedInstance) throws MetadataException;
/**
* This is upon adding a new trait to a typed instance.
*
* @param guid globally unique identifier for the entity
* @param traitName trait name for the instance that needs to be added to entity
* @throws org.apache.hadoop.metadata.MetadataException
*/
void onTraitAdded(String guid, String traitName) throws MetadataException;
/**
* This is upon deleting a trait from a typed instance.
*
* @param guid globally unique identifier for the entity
* @param traitName trait name for the instance that needs to be deleted from entity
* @throws org.apache.hadoop.metadata.MetadataException
*/
void onTraitDeleted(String guid, String traitName) throws MetadataException;
} }
...@@ -20,6 +20,9 @@ package org.apache.hadoop.metadata.repository; ...@@ -20,6 +20,9 @@ package org.apache.hadoop.metadata.repository;
import org.apache.hadoop.metadata.typesystem.IReferenceableInstance; import org.apache.hadoop.metadata.typesystem.IReferenceableInstance;
import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance; import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance;
import org.apache.hadoop.metadata.typesystem.ITypedStruct;
import org.apache.hadoop.metadata.typesystem.types.AttributeInfo;
import org.apache.hadoop.metadata.typesystem.types.IDataType;
import java.util.List; import java.util.List;
...@@ -28,12 +31,130 @@ import java.util.List; ...@@ -28,12 +31,130 @@ import java.util.List;
*/ */
public interface MetadataRepository { public interface MetadataRepository {
/**
* Returns the property key used to store entity type name.
*
* @return property key used to store entity type name.
*/
String getTypeAttributeName(); String getTypeAttributeName();
/**
* Return the property key used to store a given traitName in the repository.
*
* @param dataType data type
* @param traitName trait name
* @return property key used to store a given traitName
*/
String getTraitLabel(IDataType<?> dataType, String traitName);
/**
* Return the property key used to store a given attribute in the repository.
*
* @param dataType data type
* @param aInfo attribute info
* @return property key used to store a given attribute
*/
String getFieldNameInVertex(IDataType<?> dataType, AttributeInfo aInfo);
/**
* Return the edge label for a given attribute in the repository.
*
* @param dataType data type
* @param aInfo attribute info
* @return edge label for a given attribute
*/
String getEdgeLabel(IDataType<?> dataType, AttributeInfo aInfo);
/**
* Creates an entity definition (instance) corresponding to a given type.
*
* @param entity entity (typed instance)
* @param entityType entity type name
* @return a globally unique identifier
* @throws RepositoryException
*/
String createEntity(IReferenceableInstance entity, String createEntity(IReferenceableInstance entity,
String entityType) throws RepositoryException; String entityType) throws RepositoryException;
/**
* Fetch the complete definition of an entity given its GUID.
*
* @param guid globally unique identifier for the entity
* @return entity (typed instance) definition
* @throws RepositoryException
*/
ITypedReferenceableInstance getEntityDefinition(String guid) throws RepositoryException; ITypedReferenceableInstance getEntityDefinition(String guid) throws RepositoryException;
/**
* Gets the list of entities for a given entity type.
*
* @param entityType name of a type which is unique
* @return a list of entity names for the given type
* @throws RepositoryException
*/
List<String> getEntityList(String entityType) throws RepositoryException; List<String> getEntityList(String entityType) throws RepositoryException;
/**
* Deletes an entity definition (instance) corresponding to a given type.
*
* @param guid globally unique identifier for the entity
* @return true if deleted else false
* @throws RepositoryException
*/
// boolean deleteEntity(String guid) throws RepositoryException;
/**
* Updates an entity given its GUID with the attribute name and value.
*
* @param guid globally unique identifier for the entity
* @param attributeName name of the attribute
* @param attributeValue value of the attribute
* @return an entity instance with updated state
* @throws RepositoryException
*/
//ITypedReferenceableInstance updateEntity(String guid, String attributeName,
// String attributeValue) throws RepositoryException;
// Trait management functions
/**
* Gets the list of trait names for a given entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @return a list of trait names for the given entity guid
* @throws RepositoryException
*/
List<String> getTraitNames(String guid) throws RepositoryException;
/**
* Adds a new trait to an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitName trait name for the instance that needs to be added to entity
* @param traitInstance trait instance that needs to be added to entity
* @throws RepositoryException
*/
void addTrait(String guid, String traitName,
ITypedStruct traitInstance) throws RepositoryException;
/**
* Adds a list of traits to an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitInstances list of trait instances that needs to be added to entity
* @return an entity instance with updated traits
* @throws RepositoryException
*/
// ITypedReferenceableInstance addTraits(String guid, Map<String, ITypedStruct> traitInstances)
// throws RepositoryException;
/**
* Deletes a given trait from an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitNameToBeDeleted name of the trait
* @throws RepositoryException
*/
void deleteTrait(String guid,
String traitNameToBeDeleted) throws RepositoryException;
} }
...@@ -33,14 +33,10 @@ public final class Constants { ...@@ -33,14 +33,10 @@ public final class Constants {
public static final String ENTITY_TYPE_INDEX = "type_index"; public static final String ENTITY_TYPE_INDEX = "type_index";
/** /**
* Data type property key. * Trait names property key and index name.
*/
// public static final String DATA_TYPE_PROPERTY_KEY = "dataType";
/**
* Trait names property key.
*/ */
public static final String TRAIT_NAMES_PROPERTY_KEY = "traitNames"; public static final String TRAIT_NAMES_PROPERTY_KEY = "traitNames";
public static final String TRAIT_NAMES_INDEX = "trait_names_index";
public static final String VERSION_PROPERTY_KEY = "version"; public static final String VERSION_PROPERTY_KEY = "version";
public static final String TIMESTAMP_PROPERTY_KEY = "timestamp"; public static final String TIMESTAMP_PROPERTY_KEY = "timestamp";
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package org.apache.hadoop.metadata.repository.graph; package org.apache.hadoop.metadata.repository.graph;
import com.google.common.base.Preconditions;
import com.thinkaurelius.titan.core.TitanGraph; import com.thinkaurelius.titan.core.TitanGraph;
import com.thinkaurelius.titan.core.TitanProperty; import com.thinkaurelius.titan.core.TitanProperty;
import com.thinkaurelius.titan.core.TitanVertex; import com.thinkaurelius.titan.core.TitanVertex;
...@@ -50,7 +51,6 @@ import javax.inject.Inject; ...@@ -50,7 +51,6 @@ import javax.inject.Inject;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
...@@ -96,14 +96,17 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -96,14 +96,17 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
return Constants.ENTITY_TYPE_PROPERTY_KEY; return Constants.ENTITY_TYPE_PROPERTY_KEY;
} }
@Override
public String getTraitLabel(IDataType<?> dataType, String traitName) { public String getTraitLabel(IDataType<?> dataType, String traitName) {
return dataType.getName() + "." + traitName; return dataType.getName() + "." + traitName;
} }
@Override
public String getFieldNameInVertex(IDataType<?> dataType, AttributeInfo aInfo) { public String getFieldNameInVertex(IDataType<?> dataType, AttributeInfo aInfo) {
return dataType.getName() + "." + aInfo.name; return dataType.getName() + "." + aInfo.name;
} }
@Override
public String getEdgeLabel(IDataType<?> dataType, AttributeInfo aInfo) { public String getEdgeLabel(IDataType<?> dataType, AttributeInfo aInfo) {
return dataType.getName() + "." + aInfo.name; return dataType.getName() + "." + aInfo.name;
} }
...@@ -131,20 +134,27 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -131,20 +134,27 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
try { try {
titanGraph.rollback(); // clean up before starting a query titanGraph.rollback(); // clean up before starting a query
Vertex instanceVertex = GraphHelper.findVertexByGUID(titanGraph, guid); Vertex instanceVertex = getVertexForGUID(guid);
if (instanceVertex == null) {
LOG.debug("Could not find a vertex for guid {}", guid);
return null;
}
LOG.debug("Found a vertex {} for guid {}", instanceVertex, guid); LOG.debug("Found a vertex {} for guid {}", instanceVertex, guid);
return graphToInstanceMapper.mapGraphToTypedInstance(guid, instanceVertex); return graphToInstanceMapper.mapGraphToTypedInstance(guid, instanceVertex);
} catch (Exception e) { } catch (MetadataException e) {
throw new RepositoryException(e); throw new RepositoryException(e);
} }
} }
private Vertex getVertexForGUID(String guid) throws RepositoryException {
Vertex instanceVertex = GraphHelper.findVertexByGUID(titanGraph, guid);
if (instanceVertex == null) {
LOG.debug("Could not find a vertex for guid={}", guid);
throw new RepositoryException(
"Could not find an entity in the repository for guid: " + guid);
}
return instanceVertex;
}
@Override @Override
public List<String> getEntityList(String entityType) throws RepositoryException { public List<String> getEntityList(String entityType) throws RepositoryException {
LOG.info("Retrieving entity list for type={}", entityType); LOG.info("Retrieving entity list for type={}", entityType);
...@@ -164,18 +174,133 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -164,18 +174,133 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
return entityList; return entityList;
} }
public Id getIdFromVertex(String dataTypeName, TitanVertex vertex) { /**
* Gets the list of trait names for a given entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @return a list of trait names for the given entity guid
* @throws RepositoryException
*/
@Override
public List<String> getTraitNames(String guid) throws RepositoryException {
LOG.info("Retrieving trait names for entity={}", guid);
titanGraph.rollback(); // clean up before starting a query
Vertex instanceVertex = getVertexForGUID(guid);
return getTraitNames(instanceVertex);
}
public List<String> getTraitNames(Vertex entityVertex) {
ArrayList<String> traits = new ArrayList<>();
for (TitanProperty property : ((TitanVertex) entityVertex)
.getProperties(Constants.TRAIT_NAMES_PROPERTY_KEY)) {
traits.add((String) property.getValue());
}
return traits;
}
/**
* Adds a new trait to an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitInstance trait instance that needs to be added to entity
* @throws RepositoryException
*/
@Override
public void addTrait(String guid, String traitName,
ITypedStruct traitInstance) throws RepositoryException {
Preconditions.checkNotNull(traitInstance, "Trait instance cannot be null");
LOG.info("Adding a new trait={} for entity={}", traitName, guid);
try {
titanGraph.rollback(); // clean up before starting a query
Vertex instanceVertex = getVertexForGUID(guid);
// add the trait instance as a new vertex
final String typeName = getTypeName(instanceVertex);
instanceToGraphMapper.mapTraitInstanceToVertex(
traitName, traitInstance, getIdFromVertex(typeName, instanceVertex),
typeName, instanceVertex, Collections.<Id, Vertex>emptyMap());
// update the traits in entity once adding trait instance is successful
((TitanVertex) instanceVertex)
.addProperty(Constants.TRAIT_NAMES_PROPERTY_KEY, traitName);
titanGraph.commit(); // commit if there are no errors
} catch (MetadataException e) {
titanGraph.rollback();
throw new RepositoryException(e);
}
}
/**
* Deletes a given trait from an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitNameToBeDeleted name of the trait
* @throws RepositoryException
*/
@Override
public void deleteTrait(String guid, String traitNameToBeDeleted)
throws RepositoryException {
LOG.info("Deleting trait={} from entity={}", traitNameToBeDeleted, guid);
try {
titanGraph.rollback(); // clean up before starting a query
Vertex instanceVertex = getVertexForGUID(guid);
List<String> traitNames = getTraitNames(instanceVertex);
if (!traitNames.contains(traitNameToBeDeleted)) {
throw new RepositoryException("Could not find trait=" + traitNameToBeDeleted
+ " in the repository for entity: " + guid);
}
final String entityTypeName = getTypeName(instanceVertex);
String relationshipLabel = entityTypeName + "." + traitNameToBeDeleted;
Iterator<Edge> results = instanceVertex.getEdges(
Direction.OUT, relationshipLabel).iterator();
if (results.hasNext()) { // there should only be one edge for this label
final Edge traitEdge = results.next();
final Vertex traitVertex = traitEdge.getVertex(Direction.IN);
// remove the edge to the trait instance from the repository
titanGraph.removeEdge(traitEdge);
if (traitVertex != null) { // remove the trait instance from the repository
titanGraph.removeVertex(traitVertex);
// update the traits in entity once trait removal is successful
traitNames.remove(traitNameToBeDeleted);
updateTraits(instanceVertex, traitNames);
}
titanGraph.commit(); // commit if there are no errors
}
} catch (Exception e) {
titanGraph.rollback();
throw new RepositoryException(e);
}
}
private void updateTraits(Vertex instanceVertex, List<String> traitNames) {
// remove the key
instanceVertex.removeProperty(Constants.TRAIT_NAMES_PROPERTY_KEY);
// add it back again
for (String traitName : traitNames) {
((TitanVertex) instanceVertex).addProperty(
Constants.TRAIT_NAMES_PROPERTY_KEY, traitName);
}
}
public Id getIdFromVertex(String dataTypeName, Vertex vertex) {
return new Id( return new Id(
vertex.<String>getProperty(Constants.GUID_PROPERTY_KEY), vertex.<String>getProperty(Constants.GUID_PROPERTY_KEY),
vertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), vertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY),
dataTypeName); dataTypeName);
} }
public List<String> getTraitNames(TitanVertex vertex) { String getTypeName(Vertex instanceVertex) {
final String traitNames = vertex.getProperty(Constants.TRAIT_NAMES_PROPERTY_KEY); return instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
return traitNames == null
? Collections.<String>emptyList()
: Arrays.asList(traitNames.split(","));
} }
private final class EntityProcessor implements ObjectGraphWalker.NodeProcessor { private final class EntityProcessor implements ObjectGraphWalker.NodeProcessor {
...@@ -230,7 +355,8 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -230,7 +355,8 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
if (id.isAssigned()) { // has a GUID if (id.isAssigned()) { // has a GUID
instanceVertex = GraphHelper.findVertexByGUID(titanGraph, id.id); instanceVertex = GraphHelper.findVertexByGUID(titanGraph, id.id);
} else { } else {
instanceVertex = GraphHelper.createVertex(titanGraph, typedInstance); instanceVertex =
GraphHelper.createVertexWithIdentity(titanGraph, typedInstance);
} }
idToVertexMap.put(id, instanceVertex); idToVertexMap.put(id, instanceVertex);
...@@ -242,7 +368,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -242,7 +368,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
private final class TypedInstanceToGraphMapper { private final class TypedInstanceToGraphMapper {
private String mapTypedInstanceToGraph(IReferenceableInstance typedInstance) private String mapTypedInstanceToGraph(IReferenceableInstance typedInstance)
throws MetadataException { throws MetadataException {
EntityProcessor entityProcessor = new EntityProcessor(); EntityProcessor entityProcessor = new EntityProcessor();
try { try {
...@@ -258,7 +384,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -258,7 +384,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
return addDiscoveredInstances(typedInstance, entityProcessor, newTypedInstances); return addDiscoveredInstances(typedInstance, entityProcessor, newTypedInstances);
} }
/* /**
* Step 2: Traverse oldIdToInstance map create newInstances : * Step 2: Traverse oldIdToInstance map create newInstances :
* List[ITypedReferenceableInstance] * List[ITypedReferenceableInstance]
* - create a ITypedReferenceableInstance. * - create a ITypedReferenceableInstance.
...@@ -277,9 +403,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -277,9 +403,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
transientInstance, Multiplicity.REQUIRED); transientInstance, Multiplicity.REQUIRED);
newTypedInstances.add(newInstance); newTypedInstances.add(newInstance);
/* // Now replace old references with new Ids
* Now replace old references with new Ids
*/
MapIds mapIds = new MapIds(entityProcessor.idToNewIdMap); MapIds mapIds = new MapIds(entityProcessor.idToNewIdMap);
new ObjectGraphWalker(typeSystem, mapIds, newTypedInstances).walk(); new ObjectGraphWalker(typeSystem, mapIds, newTypedInstances).walk();
...@@ -488,8 +612,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -488,8 +612,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
case CLASS: case CLASS:
Id referenceId = (Id) value; Id referenceId = (Id) value;
mapClassReferenceAsEdge( mapClassReferenceAsEdge(
instanceVertex, idToVertexMap, instanceVertex, idToVertexMap, propertyName, referenceId);
propertyName, referenceId);
break; break;
default: default:
...@@ -521,7 +644,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -521,7 +644,7 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
Map<Id, Vertex> idToVertexMap) Map<Id, Vertex> idToVertexMap)
throws MetadataException { throws MetadataException {
// add a new vertex for the struct or trait instance // add a new vertex for the struct or trait instance
Vertex structInstanceVertex = GraphHelper.createVertex( Vertex structInstanceVertex = GraphHelper.createVertexWithoutIdentity(
graphService.getBlueprintsGraph(), structInstance.getTypeName(), id); graphService.getBlueprintsGraph(), structInstance.getTypeName(), id);
LOG.debug("created vertex {} for struct {}", structInstanceVertex, attributeInfo.name); LOG.debug("created vertex {} for struct {}", structInstanceVertex, attributeInfo.name);
...@@ -536,18 +659,28 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -536,18 +659,28 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
ITypedReferenceableInstance typedInstance, ITypedReferenceableInstance typedInstance,
Vertex parentInstanceVertex, Vertex parentInstanceVertex,
Map<Id, Vertex> idToVertexMap) Map<Id, Vertex> idToVertexMap)
throws MetadataException { throws MetadataException {
// add a new vertex for the struct or trait instance // add a new vertex for the struct or trait instance
Vertex traitInstanceVertex = GraphHelper.createVertex( mapTraitInstanceToVertex(traitName, traitInstance, typedInstance.getId(),
graphService.getBlueprintsGraph(), traitInstance, typedInstance.getId()); typedInstance.getTypeName(), parentInstanceVertex, idToVertexMap);
}
private void mapTraitInstanceToVertex(String traitName, ITypedStruct traitInstance,
Id typedInstanceId, String typedInstanceTypeName,
Vertex parentInstanceVertex,
Map<Id, Vertex> idToVertexMap)
throws MetadataException {
// add a new vertex for the struct or trait instance
Vertex traitInstanceVertex = GraphHelper.createVertexWithoutIdentity(
graphService.getBlueprintsGraph(), traitInstance.getTypeName(), typedInstanceId);
LOG.debug("created vertex {} for trait {}", traitInstanceVertex, traitName); LOG.debug("created vertex {} for trait {}", traitInstanceVertex, traitName);
// map all the attributes to this newly created vertex // map all the attributes to this newly created vertex
mapInstanceToVertex(typedInstance.getId(), traitInstance, traitInstanceVertex, mapInstanceToVertex(typedInstanceId, traitInstance, traitInstanceVertex,
traitInstance.fieldMapping().fields, idToVertexMap); traitInstance.fieldMapping().fields, idToVertexMap);
// add an edge to the newly created vertex from the parent // add an edge to the newly created vertex from the parent
String relationshipLabel = typedInstance.getTypeName() + "." + traitName; String relationshipLabel = typedInstanceTypeName + "." + traitName;
GraphHelper.addEdge( GraphHelper.addEdge(
titanGraph, parentInstanceVertex, traitInstanceVertex, relationshipLabel); titanGraph, parentInstanceVertex, traitInstanceVertex, relationshipLabel);
} }
...@@ -602,16 +735,12 @@ public class GraphBackedMetadataRepository implements MetadataRepository { ...@@ -602,16 +735,12 @@ public class GraphBackedMetadataRepository implements MetadataRepository {
public ITypedReferenceableInstance mapGraphToTypedInstance(String guid, public ITypedReferenceableInstance mapGraphToTypedInstance(String guid,
Vertex instanceVertex) Vertex instanceVertex)
throws MetadataException { throws MetadataException {
LOG.debug("Mapping graph root vertex {} to typed instance for guid {}", LOG.debug("Mapping graph root vertex {} to typed instance for guid {}",
instanceVertex, guid); instanceVertex, guid);
String typeName = instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY); String typeName = instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
List<String> traits = new ArrayList<>(); List<String> traits = getTraitNames(instanceVertex);
for (TitanProperty property : ((TitanVertex) instanceVertex)
.getProperties(Constants.TRAIT_NAMES_PROPERTY_KEY)) {
traits.add((String) property.getValue());
}
Id id = new Id(guid, Id id = new Id(guid,
instanceVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), typeName); instanceVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), typeName);
......
...@@ -24,7 +24,6 @@ import com.tinkerpop.blueprints.Edge; ...@@ -24,7 +24,6 @@ import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Graph; import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.GraphQuery; import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.Vertex;
import org.apache.hadoop.metadata.typesystem.ITypedInstance;
import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance; import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance;
import org.apache.hadoop.metadata.typesystem.persistence.Id; import org.apache.hadoop.metadata.typesystem.persistence.Id;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -43,32 +42,34 @@ public final class GraphHelper { ...@@ -43,32 +42,34 @@ public final class GraphHelper {
private GraphHelper() { private GraphHelper() {
} }
public static Vertex createVertex(Graph graph, public static Vertex createVertexWithIdentity(Graph graph,
ITypedReferenceableInstance typedInstance) { ITypedReferenceableInstance typedInstance) {
return createVertex(graph, typedInstance, typedInstance.getId()); final Vertex vertexWithIdentity = createVertexWithoutIdentity(
} graph, typedInstance.getTypeName(), typedInstance.getId());
// add identity
final String guid = UUID.randomUUID().toString();
vertexWithIdentity.setProperty(Constants.GUID_PROPERTY_KEY, guid);
public static Vertex createVertex(Graph graph, return vertexWithIdentity;
ITypedInstance typedInstance,
Id typedInstanceId) {
return createVertex(graph, typedInstance.getTypeName(), typedInstanceId);
} }
public static Vertex createVertex(Graph graph, public static Vertex createVertexWithoutIdentity(Graph graph,
String typeName, String typeName,
Id typedInstanceId) { Id typedInstanceId) {
final Vertex instanceVertex = graph.addVertex(null); final Vertex vertexWithoutIdentity = graph.addVertex(null);
// type
instanceVertex.setProperty(Constants.ENTITY_TYPE_PROPERTY_KEY, typeName);
// id // add type information
final String guid = UUID.randomUUID().toString(); vertexWithoutIdentity.setProperty(Constants.ENTITY_TYPE_PROPERTY_KEY, typeName);
instanceVertex.setProperty(Constants.GUID_PROPERTY_KEY, guid);
// add version information
vertexWithoutIdentity.setProperty(Constants.VERSION_PROPERTY_KEY, typedInstanceId.version);
// version // add timestamp information
instanceVertex.setProperty(Constants.VERSION_PROPERTY_KEY, typedInstanceId.version); vertexWithoutIdentity.setProperty(
Constants.TIMESTAMP_PROPERTY_KEY, System.currentTimeMillis());
return instanceVertex; return vertexWithoutIdentity;
} }
public static Edge addEdge(TitanGraph titanGraph, Vertex fromVertex, Vertex toVertex, public static Edge addEdge(TitanGraph titanGraph, Vertex fromVertex, Vertex toVertex,
......
...@@ -20,20 +20,21 @@ package org.apache.hadoop.metadata.services; ...@@ -20,20 +20,21 @@ package org.apache.hadoop.metadata.services;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.apache.hadoop.metadata.MetadataException; import org.apache.hadoop.metadata.MetadataException;
import org.apache.hadoop.metadata.typesystem.TypesDef;
import org.apache.hadoop.metadata.discovery.SearchIndexer; import org.apache.hadoop.metadata.discovery.SearchIndexer;
import org.apache.hadoop.metadata.typesystem.json.Serialization$;
import org.apache.hadoop.metadata.typesystem.json.TypesSerialization;
import org.apache.hadoop.metadata.listener.EntityChangeListener; import org.apache.hadoop.metadata.listener.EntityChangeListener;
import org.apache.hadoop.metadata.listener.TypesChangeListener; import org.apache.hadoop.metadata.listener.TypesChangeListener;
import org.apache.hadoop.metadata.repository.MetadataRepository; import org.apache.hadoop.metadata.repository.MetadataRepository;
import org.apache.hadoop.metadata.repository.RepositoryException; import org.apache.hadoop.metadata.repository.RepositoryException;
import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance; import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance;
import org.apache.hadoop.metadata.typesystem.ITypedStruct;
import org.apache.hadoop.metadata.typesystem.TypesDef;
import org.apache.hadoop.metadata.typesystem.json.Serialization$;
import org.apache.hadoop.metadata.typesystem.json.TypesSerialization;
import org.apache.hadoop.metadata.typesystem.types.IDataType; import org.apache.hadoop.metadata.typesystem.types.IDataType;
import org.apache.hadoop.metadata.typesystem.types.TypeSystem; import org.apache.hadoop.metadata.typesystem.types.TypeSystem;
import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject; import org.codehaus.jettison.json.JSONObject;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -81,7 +82,7 @@ public class DefaultMetadataService implements MetadataService { ...@@ -81,7 +82,7 @@ public class DefaultMetadataService implements MetadataService {
TypesDef typesDef = TypesSerialization.fromJson(typeDefinition); TypesDef typesDef = TypesSerialization.fromJson(typeDefinition);
Map<String, IDataType> typesAdded = typeSystem.defineTypes(typesDef); Map<String, IDataType> typesAdded = typeSystem.defineTypes(typesDef);
onAdd(typesAdded); onTypesAddedToRepo(typesAdded);
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
for (Map.Entry<String, IDataType> entry : typesAdded.entrySet()) { for (Map.Entry<String, IDataType> entry : typesAdded.entrySet()) {
...@@ -101,15 +102,9 @@ public class DefaultMetadataService implements MetadataService { ...@@ -101,15 +102,9 @@ public class DefaultMetadataService implements MetadataService {
Preconditions.checkNotNull(typeDefinition, "type definition cannot be null"); Preconditions.checkNotNull(typeDefinition, "type definition cannot be null");
// verify if the type already exists // verify if the type already exists
IDataType existingTypeDefinition = null; if (typeSystem.isRegistered(typeName)) {
try { LOG.error("type is already defined for {}", typeName);
existingTypeDefinition = typeSystem.getDataType(IDataType.class, typeName); throw new MetadataException("type is already defined for : " + typeName);
} catch (MetadataException ignore) {
// do nothing
}
if (existingTypeDefinition != null) {
throw new RepositoryException("type is already defined for : " + typeName);
} }
} }
...@@ -145,28 +140,16 @@ public class DefaultMetadataService implements MetadataService { ...@@ -145,28 +140,16 @@ public class DefaultMetadataService implements MetadataService {
@Override @Override
public String createEntity(String entityType, public String createEntity(String entityType,
String entityDefinition) throws MetadataException { String entityDefinition) throws MetadataException {
try { Preconditions.checkNotNull(entityDefinition, "entity cannot be null");
validateEntity(entityDefinition, entityType); Preconditions.checkNotNull(entityType, "entity type cannot be null");
ITypedReferenceableInstance entityInstance = ITypedReferenceableInstance entityInstance =
Serialization$.MODULE$.fromJson(entityDefinition); Serialization$.MODULE$.fromJson(entityDefinition);
final String guid = repository.createEntity(entityInstance, entityType); final String guid = repository.createEntity(entityInstance, entityType);
onAdd(entityType, entityInstance); onEntityAddedToRepo(entityType, entityInstance);
return guid; return guid;
} catch (ParseException e) {
LOG.error("Unable to parse JSON {} for type {}", entityDefinition, entityType, e);
throw new MetadataException("validation failed for: " + entityType);
}
}
private void validateEntity(String entity, String entityType) throws ParseException {
Preconditions.checkNotNull(entity, "entity cannot be null");
Preconditions.checkNotNull(entityType, "entity type cannot be null");
// todo: this is failing for instances but not types
// JSONValue.parseWithException(entity);
} }
/** /**
...@@ -180,9 +163,7 @@ public class DefaultMetadataService implements MetadataService { ...@@ -180,9 +163,7 @@ public class DefaultMetadataService implements MetadataService {
Preconditions.checkNotNull(guid, "guid cannot be null"); Preconditions.checkNotNull(guid, "guid cannot be null");
final ITypedReferenceableInstance instance = repository.getEntityDefinition(guid); final ITypedReferenceableInstance instance = repository.getEntityDefinition(guid);
return instance == null return Serialization$.MODULE$.toJson(instance);
? null
: Serialization$.MODULE$.toJson(instance);
} }
/** /**
...@@ -214,7 +195,65 @@ public class DefaultMetadataService implements MetadataService { ...@@ -214,7 +195,65 @@ public class DefaultMetadataService implements MetadataService {
} }
} }
private void onAdd(Map<String, IDataType> typesAdded) throws MetadataException { /**
* Gets the list of trait names for a given entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @return a list of trait names for the given entity guid
* @throws MetadataException
*/
@Override
public List<String> getTraitNames(String guid) throws MetadataException {
Preconditions.checkNotNull(guid, "entity GUID cannot be null");
return repository.getTraitNames(guid);
}
/**
* Adds a new trait to an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitName trait name for the instance that needs to be added to entity
* @param traitInstance trait instance that needs to be added to entity
* @throws MetadataException
*/
@Override
public void addTrait(String guid, String traitName,
ITypedStruct traitInstance) throws MetadataException {
Preconditions.checkNotNull(guid, "entity GUID cannot be null");
Preconditions.checkNotNull(traitName, "Trait name cannot be null");
Preconditions.checkNotNull(traitInstance, "Trait instance cannot be null");
// ensure trait type is already registered with the TS
Preconditions.checkArgument(!typeSystem.isRegistered(traitName),
"trait=%s should be defined in type system before it can be added", traitName);
repository.addTrait(guid, traitName, traitInstance);
onTraitAddedToEntity(guid, traitName);
}
/**
* Deletes a given trait from an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitNameToBeDeleted name of the trait
* @throws MetadataException
*/
@Override
public void deleteTrait(String guid,
String traitNameToBeDeleted) throws MetadataException {
Preconditions.checkNotNull(guid, "entity GUID cannot be null");
Preconditions.checkNotNull(traitNameToBeDeleted, "Trait name cannot be null");
// ensure trait type is already registered with the TS
Preconditions.checkArgument(!typeSystem.isRegistered(traitNameToBeDeleted),
"trait=%s should be defined in type system before it can be deleted",
traitNameToBeDeleted);
repository.deleteTrait(guid, traitNameToBeDeleted);
onTraitDeletedFromEntity(guid, traitNameToBeDeleted);
}
private void onTypesAddedToRepo(Map<String, IDataType> typesAdded) throws MetadataException {
for (TypesChangeListener listener : typesChangeListeners) { for (TypesChangeListener listener : typesChangeListeners) {
for (Map.Entry<String, IDataType> entry : typesAdded.entrySet()) { for (Map.Entry<String, IDataType> entry : typesAdded.entrySet()) {
listener.onAdd(entry.getKey(), entry.getValue()); listener.onAdd(entry.getKey(), entry.getValue());
...@@ -230,10 +269,26 @@ public class DefaultMetadataService implements MetadataService { ...@@ -230,10 +269,26 @@ public class DefaultMetadataService implements MetadataService {
typesChangeListeners.remove(listener); typesChangeListeners.remove(listener);
} }
private void onAdd(String typeName, private void onEntityAddedToRepo(String typeName,
ITypedReferenceableInstance typedInstance) throws MetadataException { ITypedReferenceableInstance typedInstance)
throws MetadataException {
for (EntityChangeListener listener : entityChangeListeners) {
listener.onEntityAdded(typeName, typedInstance);
}
}
private void onTraitAddedToEntity(String typeName,
String traitName) throws MetadataException {
for (EntityChangeListener listener : entityChangeListeners) {
listener.onTraitAdded(typeName, traitName);
}
}
private void onTraitDeletedFromEntity(String typeName,
String traitName) throws MetadataException {
for (EntityChangeListener listener : entityChangeListeners) { for (EntityChangeListener listener : entityChangeListeners) {
listener.onAdd(typeName, typedInstance); listener.onTraitDeleted(typeName, traitName);
} }
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
package org.apache.hadoop.metadata.services; package org.apache.hadoop.metadata.services;
import org.apache.hadoop.metadata.MetadataException; import org.apache.hadoop.metadata.MetadataException;
import org.apache.hadoop.metadata.typesystem.ITypedStruct;
import org.codehaus.jettison.json.JSONObject; import org.codehaus.jettison.json.JSONObject;
import java.util.List; import java.util.List;
...@@ -78,4 +79,35 @@ public interface MetadataService { ...@@ -78,4 +79,35 @@ public interface MetadataService {
* @return list of entity names for the given type in the repository * @return list of entity names for the given type in the repository
*/ */
List<String> getEntityList(String entityType) throws MetadataException; List<String> getEntityList(String entityType) throws MetadataException;
// Trait management functions
/**
* Gets the list of trait names for a given entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @return a list of trait names for the given entity guid
* @throws MetadataException
*/
List<String> getTraitNames(String guid) throws MetadataException;
/**
* Adds a new trait to an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitName trait name for the instance that needs to be added to entity
* @param traitInstance trait instance that needs to be added to entity
* @throws MetadataException
*/
void addTrait(String guid, String traitName,
ITypedStruct traitInstance) throws MetadataException;
/**
* Deletes a given trait from an existing entity represented by a guid.
*
* @param guid globally unique identifier for the entity
* @param traitNameToBeDeleted name of the trait
* @throws MetadataException
*/
void deleteTrait(String guid,
String traitNameToBeDeleted) throws MetadataException;
} }
...@@ -173,10 +173,12 @@ public class GraphBackedDiscoveryServiceTest { ...@@ -173,10 +173,12 @@ public class GraphBackedDiscoveryServiceTest {
{"DB, Table"}, {"DB, Table"},
/*{"DB as db1 Table where db1.name = \"Reporting\""},*/ /*{"DB as db1 Table where db1.name = \"Reporting\""},*/
{"DB name = \"Reporting\""}, {"DB name = \"Reporting\""},
{"Column as PII"}, /*
{"Table as Dimension"}, {"Column where is PII"},
{"View as Dimension"}, {"Table where is Dimension"},
{"Column as PII select Column.name"}, {"View where is Dimension"},
{"Column where is PII select Column.name"},
*/
{"Column select Column.name"}, {"Column select Column.name"},
{"from Table select Table.name"}, {"from Table select Table.name"},
}; };
......
...@@ -18,24 +18,41 @@ ...@@ -18,24 +18,41 @@
package org.apache.hadoop.metadata.repository.graph; package org.apache.hadoop.metadata.repository.graph;
import com.google.common.collect.ImmutableList;
import com.thinkaurelius.titan.core.TitanGraph; import com.thinkaurelius.titan.core.TitanGraph;
import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Compare;
import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.Vertex;
import org.apache.hadoop.metadata.RepositoryMetadataModule; import org.apache.hadoop.metadata.RepositoryMetadataModule;
import org.apache.hadoop.metadata.TestUtils; import org.apache.hadoop.metadata.TestUtils;
import org.apache.hadoop.metadata.repository.RepositoryException;
import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance; import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance;
import org.apache.hadoop.metadata.typesystem.ITypedStruct;
import org.apache.hadoop.metadata.typesystem.Referenceable; import org.apache.hadoop.metadata.typesystem.Referenceable;
import org.apache.hadoop.metadata.typesystem.Struct;
import org.apache.hadoop.metadata.typesystem.persistence.Id;
import org.apache.hadoop.metadata.typesystem.types.AttributeDefinition;
import org.apache.hadoop.metadata.typesystem.types.ClassType; import org.apache.hadoop.metadata.typesystem.types.ClassType;
import org.apache.hadoop.metadata.typesystem.types.DataTypes;
import org.apache.hadoop.metadata.typesystem.types.EnumTypeDefinition;
import org.apache.hadoop.metadata.typesystem.types.EnumValue;
import org.apache.hadoop.metadata.typesystem.types.HierarchicalTypeDefinition;
import org.apache.hadoop.metadata.typesystem.types.Multiplicity; import org.apache.hadoop.metadata.typesystem.types.Multiplicity;
import org.apache.hadoop.metadata.typesystem.types.StructTypeDefinition;
import org.apache.hadoop.metadata.typesystem.types.TraitType;
import org.apache.hadoop.metadata.typesystem.types.TypeSystem; import org.apache.hadoop.metadata.typesystem.types.TypeSystem;
import org.apache.hadoop.metadata.typesystem.types.utils.TypesUtil;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Guice; import org.testng.annotations.Guice;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import scala.actors.threadpool.Arrays;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID;
/** /**
* GraphBackedMetadataRepository test * GraphBackedMetadataRepository test
...@@ -47,66 +64,388 @@ import java.util.List; ...@@ -47,66 +64,388 @@ import java.util.List;
public class GraphBackedMetadataRepositoryTest { public class GraphBackedMetadataRepositoryTest {
private static final String ENTITY_TYPE = "Department"; private static final String ENTITY_TYPE = "Department";
private static final String DATABASE_TYPE = "hive_database";
private static final String DATABASE_NAME = "foo";
private static final String TABLE_TYPE = "hive_table";
private static final String TABLE_NAME = "bar";
private static final String CLASSIFICATION = "classification";
private static final String PII = "PII";
@Inject @Inject
private TitanGraphService titanGraphService; private TitanGraphService titanGraphService;
@Inject @Inject
private GraphBackedMetadataRepository repositoryService; private GraphBackedMetadataRepository repositoryService;
private TypeSystem ts; private TypeSystem typeSystem;
private String guid; private String guid;
@BeforeClass @BeforeClass
public void setUp() throws Exception { public void setUp() throws Exception {
typeSystem = TypeSystem.getInstance();
typeSystem.reset();
// start the injected graph service // start the injected graph service
titanGraphService.initialize(); titanGraphService.initialize();
new GraphBackedSearchIndexer(titanGraphService); new GraphBackedSearchIndexer(titanGraphService);
ts = TypeSystem.getInstance(); TestUtils.defineDeptEmployeeTypes(typeSystem);
createHiveTypes();
}
TestUtils.defineDeptEmployeeTypes(ts); /*
@AfterMethod
public void tearDown() {
dumpGraph();
} }
*/
@Test @Test
public void testSubmitEntity() throws Exception { public void testSubmitEntity() throws Exception {
Referenceable hrDept = TestUtils.createDeptEg1(ts); Referenceable hrDept = TestUtils.createDeptEg1(typeSystem);
ClassType deptType = ts.getDataType(ClassType.class, "Department"); ClassType deptType = typeSystem.getDataType(ClassType.class, "Department");
ITypedReferenceableInstance hrDept2 = deptType.convert(hrDept, Multiplicity.REQUIRED); ITypedReferenceableInstance hrDept2 = deptType.convert(hrDept, Multiplicity.REQUIRED);
guid = repositoryService.createEntity(hrDept2, ENTITY_TYPE); guid = repositoryService.createEntity(hrDept2, ENTITY_TYPE);
Assert.assertNotNull(guid); Assert.assertNotNull(guid);
dumpGraph();
}
private void dumpGraph() {
TitanGraph graph = titanGraphService.getTitanGraph();
for (Vertex v : graph.getVertices()) {
System.out.println("****v = " + GraphHelper.vertexString(v));
for (Edge e : v.getEdges(Direction.OUT)) {
System.out.println("****e = " + GraphHelper.edgeString(e));
}
}
} }
@Test(dependsOnMethods = "testSubmitEntity") @Test(dependsOnMethods = "testSubmitEntity")
public void testGetEntityDefinition() throws Exception { public void testGetEntityDefinitionForDepartment() throws Exception {
ITypedReferenceableInstance entity = repositoryService.getEntityDefinition(guid); ITypedReferenceableInstance entity = repositoryService.getEntityDefinition(guid);
Assert.assertNotNull(entity); Assert.assertNotNull(entity);
} }
@Test @Test (expectedExceptions = RepositoryException.class)
public void testGetEntityDefinitionNonExistent() throws Exception { public void testGetEntityDefinitionNonExistent() throws Exception {
ITypedReferenceableInstance entity = repositoryService.getEntityDefinition("blah"); repositoryService.getEntityDefinition("blah");
Assert.assertNull(entity); Assert.fail();
} }
@Test(enabled = false) @Test
public void testGetEntityList() throws Exception { public void testGetEntityList() throws Exception {
List<String> entityList = repositoryService.getEntityList(ENTITY_TYPE); List<String> entityList = repositoryService.getEntityList(ENTITY_TYPE);
System.out.println("entityList = " + entityList); System.out.println("entityList = " + entityList);
Assert.assertNotNull(entityList); Assert.assertNotNull(entityList);
Assert.assertEquals(entityList.size(), 1); // one department Assert.assertEquals(entityList.size(), 1); // one department
} }
@Test
public void testGetTypeAttributeName() throws Exception {
Assert.assertEquals(
repositoryService.getTypeAttributeName(), Constants.ENTITY_TYPE_PROPERTY_KEY);
}
@Test (dependsOnMethods = "testSubmitEntity")
public void testGetTraitLabel() throws Exception {
Assert.assertEquals(repositoryService.getTraitLabel(
typeSystem.getDataType(ClassType.class, TABLE_TYPE), CLASSIFICATION),
TABLE_TYPE + "." + CLASSIFICATION);
}
@Test
public void testCreateEntity() throws Exception {
Referenceable databaseInstance = new Referenceable(DATABASE_TYPE);
databaseInstance.set("name", DATABASE_NAME);
databaseInstance.set("description", "foo database");
System.out.println("databaseInstance = " + databaseInstance);
ClassType dbType = typeSystem.getDataType(ClassType.class, DATABASE_TYPE);
ITypedReferenceableInstance db = dbType.convert(databaseInstance, Multiplicity.REQUIRED);
System.out.println("db = " + db);
String dbGUID = repositoryService.createEntity(db, DATABASE_TYPE);
System.out.println("added db = " + dbGUID);
Referenceable dbInstance = new Referenceable(
dbGUID, DATABASE_TYPE, databaseInstance.getValuesMap());
ITypedReferenceableInstance table = createHiveTableInstance(dbInstance);
String tableGUID = repositoryService.createEntity(table, TABLE_TYPE);
System.out.println("added table = " + tableGUID);
}
@Test(dependsOnMethods = "testCreateEntity")
public void testGetEntityDefinition() throws Exception {
String guid = getGUID();
ITypedReferenceableInstance table = repositoryService.getEntityDefinition(guid);
System.out.println("*** table = " + table);
}
private String getGUID() {
Vertex tableVertex = getTableEntityVertex();
String guid = tableVertex.getProperty(Constants.GUID_PROPERTY_KEY);
if (guid == null) {
Assert.fail();
}
return guid;
}
private Vertex getTableEntityVertex() {
TitanGraph graph = titanGraphService.getTitanGraph();
GraphQuery query = graph.query()
.has(Constants.ENTITY_TYPE_PROPERTY_KEY, Compare.EQUAL, TABLE_TYPE);
Iterator<Vertex> results = query.vertices().iterator();
// returning one since guid should be unique
Vertex tableVertex = results.hasNext() ? results.next() : null;
if (tableVertex == null) {
Assert.fail();
}
return tableVertex;
}
@Test (dependsOnMethods = "testCreateEntity")
public void testGetTraitNames() throws Exception {
final List<String> traitNames = repositoryService.getTraitNames(getGUID());
Assert.assertEquals(traitNames.size(), 1);
Assert.assertEquals(traitNames, Arrays.asList(new String[]{CLASSIFICATION}));
}
@Test
public void testGetTraitNamesForEmptyTraits() throws Exception {
final List<String> traitNames = repositoryService.getTraitNames(guid);
Assert.assertEquals(traitNames.size(), 0);
}
@Test (expectedExceptions = RepositoryException.class)
public void testGetTraitNamesForBadEntity() throws Exception {
repositoryService.getTraitNames(UUID.randomUUID().toString());
Assert.fail();
}
@Test (dependsOnMethods = "testGetTraitNames")
public void testAddTrait() throws Exception {
final String aGUID = getGUID();
List<String> traitNames = repositoryService.getTraitNames(aGUID);
System.out.println("traitNames = " + traitNames);
Assert.assertEquals(traitNames.size(), 1);
Assert.assertTrue(traitNames.contains(CLASSIFICATION));
Assert.assertFalse(traitNames.contains(PII));
HierarchicalTypeDefinition<TraitType> piiTrait =
TypesUtil.createTraitTypeDef(PII, ImmutableList.<String>of());
TraitType traitType = typeSystem.defineTraitType(piiTrait);
ITypedStruct traitInstance = traitType.createInstance();
repositoryService.addTrait(aGUID, PII, traitInstance);
// refresh trait names
traitNames = repositoryService.getTraitNames(aGUID);
Assert.assertEquals(traitNames.size(), 2);
Assert.assertTrue(traitNames.contains(PII));
Assert.assertTrue(traitNames.contains(CLASSIFICATION));
}
@Test (expectedExceptions = NullPointerException.class)
public void testAddTraitWithNullInstance() throws Exception {
repositoryService.addTrait(getGUID(), PII, null);
Assert.fail();
}
@Test (dependsOnMethods = "testAddTrait", expectedExceptions = RepositoryException.class)
public void testAddTraitForBadEntity() throws Exception {
TraitType traitType = typeSystem.getDataType(TraitType.class, PII);
ITypedStruct traitInstance = traitType.createInstance();
repositoryService.addTrait(UUID.randomUUID().toString(), PII, traitInstance);
Assert.fail();
}
@Test (dependsOnMethods = "testAddTrait")
public void testDeleteTrait() throws Exception {
final String aGUID = getGUID();
List<String> traitNames = repositoryService.getTraitNames(aGUID);
Assert.assertEquals(traitNames.size(), 2);
Assert.assertTrue(traitNames.contains(PII));
Assert.assertTrue(traitNames.contains(CLASSIFICATION));
repositoryService.deleteTrait(aGUID, PII);
// refresh trait names
traitNames = repositoryService.getTraitNames(aGUID);
Assert.assertEquals(traitNames.size(), 1);
Assert.assertTrue(traitNames.contains(CLASSIFICATION));
Assert.assertFalse(traitNames.contains(PII));
}
@Test (expectedExceptions = RepositoryException.class)
public void testDeleteTraitForNonExistentEntity() throws Exception {
repositoryService.deleteTrait(UUID.randomUUID().toString(), PII);
Assert.fail();
}
@Test (expectedExceptions = RepositoryException.class)
public void testDeleteTraitForNonExistentTrait() throws Exception {
final String aGUID = getGUID();
repositoryService.deleteTrait(aGUID, "PCI");
Assert.fail();
}
@Test (dependsOnMethods = "testCreateEntity")
public void testGetIdFromVertex() throws Exception {
Vertex tableVertex = getTableEntityVertex();
String guid = tableVertex.getProperty(Constants.GUID_PROPERTY_KEY);
if (guid == null) {
Assert.fail();
}
Id expected = new Id(guid,
tableVertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), TABLE_TYPE);
Assert.assertEquals(repositoryService.getIdFromVertex(TABLE_TYPE, tableVertex),
expected);
}
@Test (dependsOnMethods = "testCreateEntity")
public void testGetTypeName() throws Exception {
Vertex tableVertex = getTableEntityVertex();
Assert.assertEquals(repositoryService.getTypeName(tableVertex), TABLE_TYPE);
}
/*
private void dumpGraph() {
TitanGraph graph = titanGraphService.getTitanGraph();
System.out.println("*******************Graph Dump****************************");
System.out.println("Vertices of " + graph);
for (Vertex vertex : graph.getVertices()) {
System.out.println(GraphHelper.vertexString(vertex));
}
System.out.println("Edges of " + graph);
for (Edge edge : graph.getEdges()) {
System.out.println(GraphHelper.edgeString(edge));
}
System.out.println("*******************Graph Dump****************************");
}
*/
private void createHiveTypes() throws Exception {
HierarchicalTypeDefinition<ClassType> databaseTypeDefinition =
TypesUtil.createClassTypeDef(DATABASE_TYPE,
ImmutableList.<String>of(),
TypesUtil.createUniqueRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("description", DataTypes.STRING_TYPE));
StructTypeDefinition structTypeDefinition =
new StructTypeDefinition("serdeType",
new AttributeDefinition[]{
TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("serde", DataTypes.STRING_TYPE)
});
EnumValue values[] = {
new EnumValue("MANAGED", 1),
new EnumValue("EXTERNAL", 2),
};
EnumTypeDefinition enumTypeDefinition = new EnumTypeDefinition("tableType", values);
typeSystem.defineEnumType(enumTypeDefinition);
HierarchicalTypeDefinition<ClassType> columnsDefinition =
TypesUtil.createClassTypeDef("column_type",
ImmutableList.<String>of(),
TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("type", DataTypes.STRING_TYPE));
StructTypeDefinition partitionDefinition =
new StructTypeDefinition("partition_type",
new AttributeDefinition[]{
TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
});
HierarchicalTypeDefinition<ClassType> tableTypeDefinition =
TypesUtil.createClassTypeDef(TABLE_TYPE,
ImmutableList.<String>of(),
TypesUtil.createUniqueRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("description", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("type", DataTypes.STRING_TYPE),
// enum
new AttributeDefinition("tableType", "tableType",
Multiplicity.REQUIRED, false, null),
// array of strings
new AttributeDefinition("columnNames",
String.format("array<%s>", DataTypes.STRING_TYPE.getName()),
Multiplicity.COLLECTION, false, null),
// array of classes
new AttributeDefinition("columns",
String.format("array<%s>", "column_type"),
Multiplicity.COLLECTION, true, null),
// array of structs
new AttributeDefinition("partitions",
String.format("array<%s>", "partition_type"),
Multiplicity.COLLECTION, true, null),
// struct reference
new AttributeDefinition("serde1",
"serdeType", Multiplicity.REQUIRED, false, null),
new AttributeDefinition("serde2",
"serdeType", Multiplicity.REQUIRED, false, null),
// class reference
new AttributeDefinition("database",
DATABASE_TYPE, Multiplicity.REQUIRED, true, null));
HierarchicalTypeDefinition<TraitType> classificationTypeDefinition =
TypesUtil.createTraitTypeDef(CLASSIFICATION,
ImmutableList.<String>of(),
TypesUtil.createRequiredAttrDef("tag", DataTypes.STRING_TYPE));
typeSystem.defineTypes(
ImmutableList.of(structTypeDefinition, partitionDefinition),
ImmutableList.of(classificationTypeDefinition),
ImmutableList.of(databaseTypeDefinition, columnsDefinition, tableTypeDefinition));
}
private ITypedReferenceableInstance createHiveTableInstance(
Referenceable databaseInstance) throws Exception {
Referenceable tableInstance = new Referenceable(TABLE_TYPE, CLASSIFICATION);
tableInstance.set("name", TABLE_NAME);
tableInstance.set("description", "bar table");
tableInstance.set("type", "managed");
tableInstance.set("tableType", 1); // enum
// refer to an existing class
tableInstance.set("database", databaseInstance);
ArrayList<String> columnNames = new ArrayList<>();
columnNames.add("first_name");
columnNames.add("last_name");
tableInstance.set("columnNames", columnNames);
Struct traitInstance = (Struct) tableInstance.getTrait(CLASSIFICATION);
traitInstance.set("tag", "foundation_etl");
Struct serde1Instance = new Struct("serdeType");
serde1Instance.set("name", "serde1");
serde1Instance.set("serde", "serde1");
tableInstance.set("serde1", serde1Instance);
Struct serde2Instance = new Struct("serdeType");
serde2Instance.set("name", "serde2");
serde2Instance.set("serde", "serde2");
tableInstance.set("serde2", serde2Instance);
ArrayList<Referenceable> columns = new ArrayList<>();
for (int index = 0; index < 5; index++) {
Referenceable columnInstance = new Referenceable("column_type");
columnInstance.set("name", "column_" + index);
columnInstance.set("type", "string");
columns.add(columnInstance);
}
tableInstance.set("columns", columns);
ArrayList<Struct> partitions = new ArrayList<>();
for (int index = 0; index < 5; index++) {
Struct partitionInstance = new Struct("partition_type");
partitionInstance.set("name", "partition_" + index);
partitions.add(partitionInstance);
}
tableInstance.set("partitions", partitions);
ClassType tableType = typeSystem.getDataType(ClassType.class, TABLE_TYPE);
return tableType.convert(tableInstance, Multiplicity.REQUIRED);
}
} }
/**
* 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.hadoop.metadata.repository.graph;
import com.google.common.collect.ImmutableList;
import com.thinkaurelius.titan.core.TitanGraph;
import com.thinkaurelius.titan.core.TitanVertex;
import com.tinkerpop.blueprints.Compare;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.Vertex;
import org.apache.hadoop.metadata.RepositoryMetadataModule;
import org.apache.hadoop.metadata.typesystem.ITypedReferenceableInstance;
import org.apache.hadoop.metadata.typesystem.Referenceable;
import org.apache.hadoop.metadata.typesystem.Struct;
import org.apache.hadoop.metadata.typesystem.types.AttributeDefinition;
import org.apache.hadoop.metadata.typesystem.types.ClassType;
import org.apache.hadoop.metadata.typesystem.types.DataTypes;
import org.apache.hadoop.metadata.typesystem.types.EnumTypeDefinition;
import org.apache.hadoop.metadata.typesystem.types.EnumValue;
import org.apache.hadoop.metadata.typesystem.types.HierarchicalTypeDefinition;
import org.apache.hadoop.metadata.typesystem.types.Multiplicity;
import org.apache.hadoop.metadata.typesystem.types.StructTypeDefinition;
import org.apache.hadoop.metadata.typesystem.types.TraitType;
import org.apache.hadoop.metadata.typesystem.types.TypeSystem;
import org.apache.hadoop.metadata.typesystem.types.utils.TypesUtil;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Iterator;
@Test
@Guice(modules = RepositoryMetadataModule.class)
public class GraphRepoMapperTest {
private static final String DATABASE_TYPE = "hive_database";
private static final String DATABASE_NAME = "foo";
private static final String TABLE_TYPE = "hive_table";
private static final String TABLE_NAME = "bar";
@Inject
private TitanGraphService titanGraphService;
@Inject
private GraphBackedMetadataRepository repositoryService;
private TypeSystem typeSystem;
@BeforeClass
public void setUp() throws Exception {
// start the injected graph service
titanGraphService.initialize();
new GraphBackedSearchIndexer(titanGraphService);
typeSystem = TypeSystem.getInstance();
typeSystem.reset();
createHiveTypes();
}
@AfterMethod
public void tearDown() {
dumpGraph();
}
@Test
public void testSubmitEntity() throws Exception {
Referenceable databaseInstance = new Referenceable(DATABASE_TYPE);
databaseInstance.set("name", DATABASE_NAME);
databaseInstance.set("description", "foo database");
System.out.println("databaseInstance = " + databaseInstance);
ClassType dbType = typeSystem.getDataType(ClassType.class, DATABASE_TYPE);
ITypedReferenceableInstance db = dbType.convert(databaseInstance, Multiplicity.REQUIRED);
System.out.println("db = " + db);
String dbGUID = repositoryService.createEntity(db, DATABASE_TYPE);
System.out.println("added db = " + dbGUID);
Referenceable dbInstance = new Referenceable(
dbGUID, DATABASE_TYPE, databaseInstance.getValuesMap());
ITypedReferenceableInstance table = createHiveTableInstance(dbInstance);
String tableGUID = repositoryService.createEntity(table, TABLE_TYPE);
System.out.println("added table = " + tableGUID);
}
@Test(dependsOnMethods = "testSubmitEntity")
public void testGetEntityDefinition() throws Exception {
TitanGraph graph = titanGraphService.getTitanGraph();
GraphQuery query = graph.query()
.has(Constants.ENTITY_TYPE_PROPERTY_KEY, Compare.EQUAL, TABLE_TYPE);
Iterator<Vertex> results = query.vertices().iterator();
// returning one since guid should be unique
Vertex tableVertex = results.hasNext() ? results.next() : null;
if (tableVertex == null) {
Assert.fail();
}
String guid = tableVertex.getProperty(Constants.GUID_PROPERTY_KEY);
if (guid == null) {
Assert.fail();
}
ITypedReferenceableInstance table = repositoryService.getEntityDefinition(guid);
System.out.println("*** table = " + table);
}
private void dumpGraph() {
TitanGraph graph = titanGraphService.getTitanGraph();
System.out.println("*******************Graph Dump****************************");
System.out.println("Vertices of " + graph);
for (Vertex vertex : graph.getVertices()) {
// System.out.println(GraphHelper.vertexString(vertex));
System.out.print("vertex = " + vertex + " [");
TitanVertex titanVertex = (TitanVertex) graph.getVertex(vertex.getId());
System.out.print("guid= " + titanVertex.getProperty(Constants.GUID_PROPERTY_KEY));
System.out
.print("type= " + titanVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY));
System.out.println("]");
}
System.out.println("Edges of " + graph);
for (Edge edge : graph.getEdges()) {
System.out.println(GraphHelper.edgeString(edge));
}
System.out.println("*******************Graph Dump****************************");
}
private void createHiveTypes() throws Exception {
HierarchicalTypeDefinition<ClassType> databaseTypeDefinition =
TypesUtil.createClassTypeDef(DATABASE_TYPE,
ImmutableList.<String>of(),
TypesUtil.createUniqueRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("description", DataTypes.STRING_TYPE));
StructTypeDefinition structTypeDefinition =
new StructTypeDefinition("serdeType",
new AttributeDefinition[]{
TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("serde", DataTypes.STRING_TYPE)
});
EnumValue values[] = {
new EnumValue("MANAGED", 1),
new EnumValue("EXTERNAL", 2),
};
EnumTypeDefinition enumTypeDefinition = new EnumTypeDefinition("tableType", values);
typeSystem.defineEnumType(enumTypeDefinition);
HierarchicalTypeDefinition<ClassType> columnsDefinition =
TypesUtil.createClassTypeDef("column_type",
ImmutableList.<String>of(),
TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("type", DataTypes.STRING_TYPE));
StructTypeDefinition partitionDefinition =
new StructTypeDefinition("partition_type",
new AttributeDefinition[]{
TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE),
});
HierarchicalTypeDefinition<ClassType> tableTypeDefinition =
TypesUtil.createClassTypeDef(TABLE_TYPE,
ImmutableList.<String>of(),
TypesUtil.createUniqueRequiredAttrDef("name", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("description", DataTypes.STRING_TYPE),
TypesUtil.createRequiredAttrDef("type", DataTypes.STRING_TYPE),
// enum
new AttributeDefinition("tableType", "tableType",
Multiplicity.REQUIRED, false, null),
// array of strings
new AttributeDefinition("columnNames",
String.format("array<%s>", DataTypes.STRING_TYPE.getName()),
Multiplicity.COLLECTION, false, null),
// array of classes
new AttributeDefinition("columns",
String.format("array<%s>", "column_type"),
Multiplicity.COLLECTION, true, null),
// array of structs
new AttributeDefinition("partitions",
String.format("array<%s>", "partition_type"),
Multiplicity.COLLECTION, true, null),
// struct reference
new AttributeDefinition("serde1",
"serdeType", Multiplicity.REQUIRED, false, null),
new AttributeDefinition("serde2",
"serdeType", Multiplicity.REQUIRED, false, null),
// class reference
new AttributeDefinition("database",
DATABASE_TYPE, Multiplicity.REQUIRED, true, null));
HierarchicalTypeDefinition<TraitType> classificationTypeDefinition =
TypesUtil.createTraitTypeDef("classification",
ImmutableList.<String>of(),
TypesUtil.createRequiredAttrDef("tag", DataTypes.STRING_TYPE));
typeSystem.defineTypes(
ImmutableList.of(structTypeDefinition, partitionDefinition),
ImmutableList.of(classificationTypeDefinition),
ImmutableList.of(databaseTypeDefinition, columnsDefinition, tableTypeDefinition));
}
private ITypedReferenceableInstance createHiveTableInstance(
Referenceable databaseInstance) throws Exception {
/*
Referenceable databaseInstance = new Referenceable(DATABASE_TYPE);
databaseInstance.set("name", DATABASE_NAME);
databaseInstance.set("description", "foo database");
*/
Referenceable tableInstance = new Referenceable(TABLE_TYPE, "classification");
tableInstance.set("name", TABLE_NAME);
tableInstance.set("description", "bar table");
tableInstance.set("type", "managed");
tableInstance.set("tableType", 1); // enum
// refer to an existing class
tableInstance.set("database", databaseInstance);
ArrayList<String> columnNames = new ArrayList<>();
columnNames.add("first_name");
columnNames.add("last_name");
tableInstance.set("columnNames", columnNames);
Struct traitInstance = (Struct) tableInstance.getTrait("classification");
traitInstance.set("tag", "foundation_etl");
Struct serde1Instance = new Struct("serdeType");
serde1Instance.set("name", "serde1");
serde1Instance.set("serde", "serde1");
tableInstance.set("serde1", serde1Instance);
Struct serde2Instance = new Struct("serdeType");
serde2Instance.set("name", "serde2");
serde2Instance.set("serde", "serde2");
tableInstance.set("serde2", serde2Instance);
ArrayList<Referenceable> columns = new ArrayList<>();
for (int index = 0; index < 5; index++) {
Referenceable columnInstance = new Referenceable("column_type");
columnInstance.set("name", "column_" + index);
columnInstance.set("type", "string");
columns.add(columnInstance);
}
tableInstance.set("columns", columns);
ArrayList<Struct> partitions = new ArrayList<>();
for (int index = 0; index < 5; index++) {
Struct partitionInstance = new Struct("partition_type");
partitionInstance.set("name", "partition_" + index);
partitions.add(partitionInstance);
}
tableInstance.set("partitions", partitions);
ClassType tableType = typeSystem.getDataType(ClassType.class, TABLE_TYPE);
return tableType.convert(tableInstance, Multiplicity.REQUIRED);
}
}
...@@ -81,6 +81,10 @@ public class TypeSystem { ...@@ -81,6 +81,10 @@ public class TypeSystem {
types.put(DataTypes.STRING_TYPE.getName(), DataTypes.STRING_TYPE); types.put(DataTypes.STRING_TYPE.getName(), DataTypes.STRING_TYPE);
} }
public boolean isRegistered(String typeName) {
return types.containsKey(typeName);
}
public <T> T getDataType(Class<T> cls, String name) throws MetadataException { public <T> T getDataType(Class<T> cls, String name) throws MetadataException {
if (types.containsKey(name)) { if (types.containsKey(name)) {
return cls.cast(types.get(name)); return cls.cast(types.get(name));
......
...@@ -52,6 +52,11 @@ ...@@ -52,6 +52,11 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.apache.hadoop.metadata</groupId> <groupId>org.apache.hadoop.metadata</groupId>
<artifactId>metadata-typesystem</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop.metadata</groupId>
<artifactId>metadata-repository</artifactId> <artifactId>metadata-repository</artifactId>
</dependency> </dependency>
......
...@@ -123,7 +123,14 @@ public class EntityResource { ...@@ -123,7 +123,14 @@ public class EntityResource {
return Response.status(status).entity(response).build(); return Response.status(status).entity(response).build();
} catch (Exception e) { } catch (MetadataException e) {
LOG.error("An entity with GUID={} does not exist", guid, e);
throw new WebApplicationException(e, Response
.status(Response.Status.NOT_FOUND)
.entity(e.getMessage())
.type(MediaType.APPLICATION_JSON)
.build());
} catch (JSONException e) {
LOG.error("Unable to get instance definition for GUID {}", guid, e); LOG.error("Unable to get instance definition for GUID {}", guid, e);
throw new WebApplicationException(e, Response throw new WebApplicationException(e, Response
.status(Response.Status.INTERNAL_SERVER_ERROR) .status(Response.Status.INTERNAL_SERVER_ERROR)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment