/** * 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 @GraphTransaction public void addTrait(String guid, ITypedStruct traitInstance) throws RepositoryException { Preconditions.checkNotNull(traitInstance, "Trait instance cannot be null"); final String traitName = traitInstance.getTypeName(); LOG.debug("Adding a new trait={} for entity={}", traitName, guid); try { Vertex instanceVertex = graphHelper.getVertexForGUID(guid); // add the trait instance as a new vertex final String typeName = GraphHelper.getTypeName(instanceVertex); TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); instanceToGraphMapper.mapTraitInstanceToVertex( traitInstance, typeSystem.getDataType(ClassType.class, typeName), instanceVertex); // update the traits in entity once adding trait instance is successful GraphHelper.addProperty(instanceVertex, Constants.TRAIT_NAMES_PROPERTY_KEY, traitName); GraphHelper.setProperty( instanceVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime()); } catch (RepositoryException e) { throw e; } catch (Exception e) { throw new RepositoryException(e); } }
private void verifyRestoredClassType(TypesDef types, String typeName) throws AtlasException { boolean clsTypeFound = false; List<HierarchicalTypeDefinition<ClassType>> classTypes = types.classTypesAsJavaList(); for (HierarchicalTypeDefinition<ClassType> classType : classTypes) { if (classType.typeName.equals(typeName)) { ClassType expectedType = ts.getDataType(ClassType.class, classType.typeName); Assert.assertEquals( expectedType.immediateAttrs.size(), classType.attributeDefinitions.length); Assert.assertEquals(expectedType.superTypes.size(), classType.superTypes.size()); Assert.assertEquals(classType.typeDescription, classType.typeName + DESCRIPTION); clsTypeFound = true; } } Assert.assertTrue(clsTypeFound, typeName + " type not restored"); }
@Test(dependsOnMethods = "testStore") public void testRestore() throws Exception { TypesDef types = typeStore.restore(); // validate enum List<EnumTypeDefinition> enumTypes = types.enumTypesAsJavaList(); Assert.assertEquals(1, enumTypes.size()); EnumTypeDefinition orgLevel = enumTypes.get(0); Assert.assertEquals(orgLevel.name, "OrgLevel"); Assert.assertEquals(orgLevel.description, "OrgLevel" + DESCRIPTION); Assert.assertEquals(orgLevel.enumValues.length, 2); EnumValue enumValue = orgLevel.enumValues[0]; Assert.assertEquals(enumValue.value, "L1"); Assert.assertEquals(enumValue.ordinal, 1); // validate class List<StructTypeDefinition> structTypes = types.structTypesAsJavaList(); Assert.assertEquals(1, structTypes.size()); verifyRestoredClassType(types, "Manager"); // validate trait List<HierarchicalTypeDefinition<TraitType>> traitTypes = types.traitTypesAsJavaList(); Assert.assertEquals(1, traitTypes.size()); HierarchicalTypeDefinition<TraitType> trait = traitTypes.get(0); Assert.assertEquals("SecurityClearance", trait.typeName); Assert.assertEquals(trait.typeName + DESCRIPTION, trait.typeDescription); Assert.assertEquals(1, trait.attributeDefinitions.length); AttributeDefinition attribute = trait.attributeDefinitions[0]; Assert.assertEquals("level", attribute.name); Assert.assertEquals(DataTypes.INT_TYPE.getName(), attribute.dataTypeName); // validate the new types ts.reset(); ts.defineTypes(types); }
@Override @GraphTransaction public ITypedReferenceableInstance getEntityDefinition( String entityType, String attribute, Object value) throws AtlasException { LOG.debug("Retrieving entity with type={} and {}={}", entityType, attribute, value); IDataType type = typeSystem.getDataType(IDataType.class, entityType); String propertyKey = getFieldNameInVertex(type, attribute); Vertex instanceVertex = graphHelper.findVertex( propertyKey, value, Constants.ENTITY_TYPE_PROPERTY_KEY, entityType, Constants.STATE_PROPERTY_KEY, Id.EntityState.ACTIVE.name()); String guid = instanceVertex.getProperty(Constants.GUID_PROPERTY_KEY); return graphToInstanceMapper.mapGraphToTypedInstance(guid, instanceVertex); }
/** Utility class for graph operations. */ public final class GraphHelper { private static final Logger LOG = LoggerFactory.getLogger(GraphHelper.class); public static final String EDGE_LABEL_PREFIX = "__"; private static final TypeSystem typeSystem = TypeSystem.getInstance(); private static final GraphHelper INSTANCE = new GraphHelper(TitanGraphProvider.getGraphInstance()); private TitanGraph titanGraph; private GraphHelper(TitanGraph titanGraph) { this.titanGraph = titanGraph; } public static GraphHelper getInstance() { return INSTANCE; } public Vertex createVertexWithIdentity( ITypedReferenceableInstance typedInstance, Set<String> superTypeNames) { final Vertex vertexWithIdentity = createVertexWithoutIdentity( typedInstance.getTypeName(), typedInstance.getId(), superTypeNames); // add identity final String guid = UUID.randomUUID().toString(); setProperty(vertexWithIdentity, Constants.GUID_PROPERTY_KEY, guid); // add version information setProperty(vertexWithIdentity, Constants.VERSION_PROPERTY_KEY, typedInstance.getId().version); return vertexWithIdentity; } public Vertex createVertexWithoutIdentity( String typeName, Id typedInstanceId, Set<String> superTypeNames) { LOG.debug( "Creating vertex for type {} id {}", typeName, typedInstanceId != null ? typedInstanceId._getId() : null); final Vertex vertexWithoutIdentity = titanGraph.addVertex(null); // add type information setProperty(vertexWithoutIdentity, Constants.ENTITY_TYPE_PROPERTY_KEY, typeName); // add super types for (String superTypeName : superTypeNames) { addProperty(vertexWithoutIdentity, Constants.SUPER_TYPES_PROPERTY_KEY, superTypeName); } // add timestamp information setProperty( vertexWithoutIdentity, Constants.TIMESTAMP_PROPERTY_KEY, System.currentTimeMillis()); return vertexWithoutIdentity; } public Edge addEdge(Vertex fromVertex, Vertex toVertex, String edgeLabel) { LOG.debug("Adding edge for {} -> label {} -> {}", fromVertex, edgeLabel, toVertex); return titanGraph.addEdge(null, fromVertex, toVertex, edgeLabel); } public Vertex findVertex(String propertyKey, Object value) { LOG.debug("Finding vertex for {}={}", propertyKey, value); GraphQuery query = titanGraph.query().has(propertyKey, value); Iterator<Vertex> results = query.vertices().iterator(); // returning one since entityType, qualifiedName should be unique return results.hasNext() ? results.next() : null; } public static Iterable<Edge> getOutGoingEdgesByLabel(Vertex instanceVertex, String edgeLabel) { if (instanceVertex != null && edgeLabel != null) { return instanceVertex.getEdges(Direction.OUT, edgeLabel); } return null; } public Edge getOutGoingEdgeById(String edgeId) { if (edgeId != null) { return titanGraph.getEdge(edgeId); } return null; } public static String vertexString(final Vertex vertex) { StringBuilder properties = new StringBuilder(); for (String propertyKey : vertex.getPropertyKeys()) { properties .append(propertyKey) .append("=") .append(vertex.getProperty(propertyKey).toString()) .append(", "); } return "v[" + vertex.getId() + "], Properties[" + properties + "]"; } public static String edgeString(final Edge edge) { return "e[" + edge.getLabel() + "], [" + edge.getVertex(Direction.OUT) + " -> " + edge.getLabel() + " -> " + edge.getVertex(Direction.IN) + "]"; } public static void setProperty(Vertex vertex, String propertyName, Object value) { LOG.debug("Setting property {} = \"{}\" to vertex {}", propertyName, value, vertex); Object existValue = vertex.getProperty(propertyName); if (value == null || (value instanceof Collection && ((Collection) value).isEmpty())) { if (existValue != null) { LOG.info("Removing property - {} value from vertex {}", propertyName, vertex); vertex.removeProperty(propertyName); } } else { if (!value.equals(existValue)) { vertex.setProperty(propertyName, value); } } } public static void addProperty(Vertex vertex, String propertyName, Object value) { LOG.debug("Setting property {} = \"{}\" to vertex {}", propertyName, value, vertex); ((TitanVertex) vertex).addProperty(propertyName, value); } public Edge removeRelation(String edgeId, boolean cascade) { LOG.debug("Removing edge with id {}", edgeId); final Edge edge = titanGraph.getEdge(edgeId); titanGraph.removeEdge(edge); LOG.info("Removed edge {}", edge); if (cascade) { Vertex referredVertex = edge.getVertex(Direction.IN); titanGraph.removeVertex(referredVertex); LOG.info("Removed vertex {}", referredVertex); } return edge; } public Vertex getVertexForGUID(String guid) throws EntityNotFoundException { return getVertexForProperty(Constants.GUID_PROPERTY_KEY, guid); } public Vertex getVertexForProperty(String propertyKey, Object value) throws EntityNotFoundException { Vertex instanceVertex = findVertex(propertyKey, value); if (instanceVertex == null) { LOG.debug("Could not find a vertex with {}={}", propertyKey, value); throw new EntityNotFoundException( "Could not find an entity in the repository with " + propertyKey + "=" + value); } else { LOG.debug("Found a vertex {} with {}={}", instanceVertex, propertyKey, value); } return instanceVertex; } public static String getQualifiedFieldName( ITypedInstance typedInstance, AttributeInfo attributeInfo) throws AtlasException { IDataType dataType = typeSystem.getDataType(IDataType.class, typedInstance.getTypeName()); return getQualifiedFieldName(dataType, attributeInfo.name); } public static String getQualifiedFieldName(IDataType dataType, String attributeName) throws AtlasException { return dataType.getTypeCategory() == DataTypes.TypeCategory.STRUCT ? dataType.getName() + "." + attributeName // else class or trait : ((HierarchicalType) dataType).getQualifiedName(attributeName); } public static String getTraitLabel(String typeName, String attrName) { return typeName + "." + attrName; } public static 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; } public static String getEdgeLabel(ITypedInstance typedInstance, AttributeInfo aInfo) throws AtlasException { IDataType dataType = typeSystem.getDataType(IDataType.class, typedInstance.getTypeName()); return getEdgeLabel(dataType, aInfo); } public static String getEdgeLabel(IDataType dataType, AttributeInfo aInfo) throws AtlasException { return GraphHelper.EDGE_LABEL_PREFIX + getQualifiedFieldName(dataType, aInfo.name); } public static Id getIdFromVertex(String dataTypeName, Vertex vertex) { return new Id( vertex.<String>getProperty(Constants.GUID_PROPERTY_KEY), vertex.<Integer>getProperty(Constants.VERSION_PROPERTY_KEY), dataTypeName); } public static String getTypeName(Vertex instanceVertex) { return instanceVertex.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY); } /** * For the given type, finds an unique attribute and checks if there is an existing instance with * the same unique value * * @param classType * @param instance * @return * @throws AtlasException */ public Vertex getVertexForInstanceByUniqueAttribute( ClassType classType, IReferenceableInstance instance) throws AtlasException { Vertex result = null; for (AttributeInfo attributeInfo : classType.fieldMapping().fields.values()) { if (attributeInfo.isUnique) { String propertyKey = getQualifiedFieldName(classType, attributeInfo.name); try { result = getVertexForProperty(propertyKey, instance.get(attributeInfo.name)); LOG.debug( "Found vertex by unique attribute : " + propertyKey + "=" + instance.get(attributeInfo.name)); } catch (EntityNotFoundException e) { // Its ok if there is no entity with the same unique value } } } return result; } public static void dumpToLog(final Graph graph) { LOG.debug("*******************Graph Dump****************************"); LOG.debug("Vertices of {}", graph); for (Vertex vertex : graph.getVertices()) { LOG.debug(vertexString(vertex)); } LOG.debug("Edges of {}", graph); for (Edge edge : graph.getEdges()) { LOG.debug(edgeString(edge)); } LOG.debug("*******************Graph Dump****************************"); } }
public static String getEdgeLabel(ITypedInstance typedInstance, AttributeInfo aInfo) throws AtlasException { IDataType dataType = typeSystem.getDataType(IDataType.class, typedInstance.getTypeName()); return getEdgeLabel(dataType, aInfo); }
public static String getQualifiedFieldName( ITypedInstance typedInstance, AttributeInfo attributeInfo) throws AtlasException { IDataType dataType = typeSystem.getDataType(IDataType.class, typedInstance.getTypeName()); return getQualifiedFieldName(dataType, attributeInfo.name); }
protected AttributeInfo getAttributeForEdge(String edgLabel) throws AtlasException { AtlasEdgeLabel atlasEdgeLabel = new AtlasEdgeLabel(edgLabel); IDataType referenceType = typeSystem.getDataType(IDataType.class, atlasEdgeLabel.getTypeName()); return getFieldMapping(referenceType).fields.get(atlasEdgeLabel.getAttributeName()); }
/** * Deletes the edge between outvertex and inVertex. The edge is for attribute attributeName of * outVertex * * @param outVertex * @param inVertex * @param attributeName * @throws AtlasException */ protected void deleteEdgeBetweenVertices(Vertex outVertex, Vertex inVertex, String attributeName) throws AtlasException { LOG.debug( "Removing edge from {} to {} with attribute name {}", string(outVertex), string(inVertex), attributeName); String typeName = GraphHelper.getTypeName(outVertex); String outId = GraphHelper.getIdFromVertex(outVertex); Id.EntityState state = GraphHelper.getState(outVertex); if ((outId != null && RequestContext.get().isDeletedEntity(outId)) || state == Id.EntityState.DELETED) { // If the reference vertex is marked for deletion, skip updating the reference return; } IDataType type = typeSystem.getDataType(IDataType.class, typeName); AttributeInfo attributeInfo = getFieldMapping(type).fields.get(attributeName); String propertyName = GraphHelper.getQualifiedFieldName(type, attributeName); String edgeLabel = EDGE_LABEL_PREFIX + propertyName; Edge edge = null; switch (attributeInfo.dataType().getTypeCategory()) { case CLASS: // If its class attribute, its the only edge between two vertices if (attributeInfo.multiplicity.nullAllowed()) { edge = GraphHelper.getEdgeForLabel(outVertex, edgeLabel); if (shouldUpdateReverseAttribute) { GraphHelper.setProperty(outVertex, propertyName, null); } } else { // Cannot unset a required attribute. throw new NullRequiredAttributeException( "Cannot unset required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " edge = " + edgeLabel); } break; case ARRAY: // If its array attribute, find the right edge between the two vertices and update array // property List<String> elements = outVertex.getProperty(propertyName); if (elements != null) { elements = new ArrayList<>( elements); // Make a copy, else list.remove reflects on titan.getProperty() for (String elementEdgeId : elements) { Edge elementEdge = graphHelper.getEdgeByEdgeId(outVertex, edgeLabel, elementEdgeId); if (elementEdge == null) { continue; } Vertex elementVertex = elementEdge.getVertex(Direction.IN); if (elementVertex.getId().toString().equals(inVertex.getId().toString())) { edge = elementEdge; // TODO element.size includes deleted items as well. should exclude if (!attributeInfo.multiplicity.nullAllowed() && elements.size() <= attributeInfo.multiplicity.lower) { // Deleting this edge would violate the attribute's lower bound. throw new NullRequiredAttributeException( "Cannot remove array element from required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(elementEdge)); } if (shouldUpdateReverseAttribute) { // if composite attribute, remove the reference as well. else, just remove the edge // for example, when table is deleted, process still references the table // but when column is deleted, table will not reference the deleted column LOG.debug( "Removing edge {} from the array attribute {}", string(elementEdge), attributeName); elements.remove(elementEdge.getId().toString()); GraphHelper.setProperty(outVertex, propertyName, elements); break; } } } } break; case MAP: // If its map attribute, find the right edge between two vertices and update map property List<String> keys = outVertex.getProperty(propertyName); if (keys != null) { keys = new ArrayList<>( keys); // Make a copy, else list.remove reflects on titan.getProperty() for (String key : keys) { String keyPropertyName = GraphHelper.getQualifiedNameForMapKey(propertyName, key); String mapEdgeId = outVertex.getProperty(keyPropertyName); Edge mapEdge = graphHelper.getEdgeByEdgeId(outVertex, keyPropertyName, mapEdgeId); Vertex mapVertex = mapEdge.getVertex(Direction.IN); if (mapVertex.getId().toString().equals(inVertex.getId().toString())) { // TODO keys.size includes deleted items as well. should exclude if (attributeInfo.multiplicity.nullAllowed() || keys.size() > attributeInfo.multiplicity.lower) { edge = mapEdge; } else { // Deleting this entry would violate the attribute's lower bound. throw new NullRequiredAttributeException( "Cannot remove map entry " + keyPropertyName + " from required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(mapEdge)); } if (shouldUpdateReverseAttribute) { // remove this key LOG.debug( "Removing edge {}, key {} from the map attribute {}", string(mapEdge), key, attributeName); keys.remove(key); GraphHelper.setProperty(outVertex, propertyName, keys); GraphHelper.setProperty(outVertex, keyPropertyName, null); } break; } } } break; case STRUCT: case TRAIT: break; default: throw new IllegalStateException( "There can't be an edge from " + string(outVertex) + " to " + string(inVertex) + " with attribute name " + attributeName + " which is not class/array/map attribute"); } if (edge != null) { deleteEdge(edge, false); RequestContext requestContext = RequestContext.get(); GraphHelper.setProperty( outVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, requestContext.getRequestTime()); requestContext.recordEntityUpdate(outId); } }
/** * Deleting any type vertex. Goes over the complex attributes and removes the references * * @param instanceVertex * @throws AtlasException */ protected void deleteTypeVertex(Vertex instanceVertex, boolean force) throws AtlasException { LOG.debug("Deleting {}", string(instanceVertex)); String typeName = GraphHelper.getTypeName(instanceVertex); IDataType type = typeSystem.getDataType(IDataType.class, typeName); FieldMapping fieldMapping = getFieldMapping(type); for (AttributeInfo attributeInfo : fieldMapping.fields.values()) { LOG.debug("Deleting attribute {} for {}", attributeInfo.name, string(instanceVertex)); String edgeLabel = GraphHelper.getEdgeLabel(type, attributeInfo); switch (attributeInfo.dataType().getTypeCategory()) { case CLASS: // If its class attribute, delete the reference deleteEdgeReference( instanceVertex, edgeLabel, DataTypes.TypeCategory.CLASS, attributeInfo.isComposite); break; case STRUCT: // If its struct attribute, delete the reference deleteEdgeReference(instanceVertex, edgeLabel, DataTypes.TypeCategory.STRUCT, false); break; case ARRAY: // For array attribute, if the element is struct/class, delete all the references IDataType elementType = ((DataTypes.ArrayType) attributeInfo.dataType()).getElemType(); DataTypes.TypeCategory elementTypeCategory = elementType.getTypeCategory(); if (elementTypeCategory == DataTypes.TypeCategory.STRUCT || elementTypeCategory == DataTypes.TypeCategory.CLASS) { Iterator<Edge> edges = GraphHelper.getOutGoingEdgesByLabel(instanceVertex, edgeLabel); if (edges != null) { while (edges.hasNext()) { Edge edge = edges.next(); deleteEdgeReference( edge, elementType.getTypeCategory(), attributeInfo.isComposite, false); } } } break; case MAP: // For map attribute, if the value type is struct/class, delete all the references DataTypes.MapType mapType = (DataTypes.MapType) attributeInfo.dataType(); DataTypes.TypeCategory valueTypeCategory = mapType.getValueType().getTypeCategory(); String propertyName = GraphHelper.getQualifiedFieldName(type, attributeInfo.name); if (valueTypeCategory == DataTypes.TypeCategory.STRUCT || valueTypeCategory == DataTypes.TypeCategory.CLASS) { List<String> keys = instanceVertex.getProperty(propertyName); if (keys != null) { for (String key : keys) { String mapEdgeLabel = GraphHelper.getQualifiedNameForMapKey(edgeLabel, key); deleteEdgeReference( instanceVertex, mapEdgeLabel, valueTypeCategory, attributeInfo.isComposite); } } } } } deleteVertex(instanceVertex, force); }
@Test public void testStore() throws AtlasException { ImmutableList<String> typeNames = ts.getTypeNames(); typeStore.store(ts, typeNames); dumpGraph(); }
@AfterClass public void tearDown() throws Exception { ts.reset(); AtlasGraphProvider.cleanup(); }
@BeforeClass public void setUp() throws Exception { ts = TypeSystem.getInstance(); ts.reset(); TestUtils.defineDeptEmployeeTypes(ts); }
@Test(dependsOnMethods = "testStore") public void testTypeUpdate() throws Exception { // Add enum value String _description = "_description_updated"; EnumTypeDefinition orgLevelEnum = new EnumTypeDefinition( "OrgLevel", "OrgLevel" + _description, new EnumValue("L1", 1), new EnumValue("L2", 2), new EnumValue("L3", 3)); // Add attribute StructTypeDefinition addressDetails = createStructTypeDef( "Address", createRequiredAttrDef("street", DataTypes.STRING_TYPE), createRequiredAttrDef("city", DataTypes.STRING_TYPE), createOptionalAttrDef("state", DataTypes.STRING_TYPE)); HierarchicalTypeDefinition<ClassType> deptTypeDef = createClassTypeDef( "Department", "Department" + _description, ImmutableSet.<String>of(), createRequiredAttrDef("name", DataTypes.STRING_TYPE), new AttributeDefinition( "employees", String.format("array<%s>", "Person"), Multiplicity.OPTIONAL, true, "department")); TypesDef typesDef = TypesUtil.getTypesDef( ImmutableList.of(orgLevelEnum), ImmutableList.of(addressDetails), ImmutableList.<HierarchicalTypeDefinition<TraitType>>of(), ImmutableList.of(deptTypeDef)); Map<String, IDataType> typesAdded = ts.updateTypes(typesDef); typeStore.store(ts, ImmutableList.copyOf(typesAdded.keySet())); verifyEdges(); // Validate the updated types TypesDef types = typeStore.restore(); ts.reset(); ts.defineTypes(types); // Assert new enum value EnumType orgLevel = ts.getDataType(EnumType.class, orgLevelEnum.name); Assert.assertEquals(orgLevel.name, orgLevelEnum.name); Assert.assertEquals(orgLevel.description, orgLevelEnum.description); Assert.assertEquals(orgLevel.values().size(), orgLevelEnum.enumValues.length); Assert.assertEquals(orgLevel.fromValue("L3").ordinal, 3); // Assert new attribute StructType addressType = ts.getDataType(StructType.class, addressDetails.typeName); Assert.assertEquals(addressType.numFields, 3); Assert.assertEquals( addressType.fieldMapping.fields.get("state").dataType(), DataTypes.STRING_TYPE); // Updating the definition again shouldn't add another edge typesDef = TypesUtil.getTypesDef( ImmutableList.<EnumTypeDefinition>of(), ImmutableList.<StructTypeDefinition>of(), ImmutableList.<HierarchicalTypeDefinition<TraitType>>of(), ImmutableList.of(deptTypeDef)); typesAdded = ts.updateTypes(typesDef); typeStore.store(ts, ImmutableList.copyOf(typesAdded.keySet())); verifyEdges(); }
/** An implementation backed by a Graph database provided as a Graph Service. */ @Singleton public class GraphBackedMetadataRepository implements MetadataRepository { private static final Logger LOG = LoggerFactory.getLogger(GraphBackedMetadataRepository.class); private static TypeSystem typeSystem = TypeSystem.getInstance(); private static final GraphHelper graphHelper = GraphHelper.getInstance(); private final TitanGraph titanGraph; private DeleteHandler deleteHandler; private GraphToTypedInstanceMapper graphToInstanceMapper; @Inject public GraphBackedMetadataRepository( GraphProvider<TitanGraph> graphProvider, DeleteHandler deleteHandler) { this.titanGraph = graphProvider.get(); graphToInstanceMapper = new GraphToTypedInstanceMapper(titanGraph); this.deleteHandler = deleteHandler; } public GraphToTypedInstanceMapper getGraphToInstanceMapper() { return graphToInstanceMapper; } @Override public String getTypeAttributeName() { return Constants.ENTITY_TYPE_PROPERTY_KEY; } @Override public String getStateAttributeName() { return Constants.STATE_PROPERTY_KEY; } /** * Returns the property key used to store super type names. * * @return property key used to store super type names. */ @Override public String getSuperTypeAttributeName() { return Constants.SUPER_TYPES_PROPERTY_KEY; } public String getIdAttributeName() { return Constants.GUID_PROPERTY_KEY; } @Override public String getTraitLabel(IDataType<?> dataType, String traitName) { return GraphHelper.getTraitLabel(dataType.getName(), traitName); } @Override public String getFieldNameInVertex(IDataType<?> dataType, AttributeInfo aInfo) throws AtlasException { if (aInfo.name.startsWith(Constants.INTERNAL_PROPERTY_KEY_PREFIX)) { return aInfo.name; } return GraphHelper.getQualifiedFieldName(dataType, aInfo.name); } public String getFieldNameInVertex(IDataType<?> dataType, String attrName) throws AtlasException { return GraphHelper.getQualifiedFieldName(dataType, attrName); } @Override public String getEdgeLabel(IDataType<?> dataType, AttributeInfo aInfo) throws AtlasException { return GraphHelper.getEdgeLabel(dataType, aInfo); } @Override @GraphTransaction public List<String> createEntities(ITypedReferenceableInstance... entities) throws RepositoryException, EntityExistsException { LOG.debug("adding entities={}", entities); try { TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); instanceToGraphMapper.mapTypedInstanceToGraph( TypedInstanceToGraphMapper.Operation.CREATE, entities); return RequestContext.get().getCreatedEntityIds(); } catch (EntityExistsException e) { throw e; } catch (AtlasException e) { throw new RepositoryException(e); } } @Override @GraphTransaction public ITypedReferenceableInstance getEntityDefinition(String guid) throws RepositoryException, EntityNotFoundException { LOG.debug("Retrieving entity with guid={}", guid); Vertex instanceVertex = graphHelper.getVertexForGUID(guid); try { return graphToInstanceMapper.mapGraphToTypedInstance(guid, instanceVertex); } catch (AtlasException e) { throw new RepositoryException(e); } } @Override @GraphTransaction public ITypedReferenceableInstance getEntityDefinition( String entityType, String attribute, Object value) throws AtlasException { LOG.debug("Retrieving entity with type={} and {}={}", entityType, attribute, value); IDataType type = typeSystem.getDataType(IDataType.class, entityType); String propertyKey = getFieldNameInVertex(type, attribute); Vertex instanceVertex = graphHelper.findVertex( propertyKey, value, Constants.ENTITY_TYPE_PROPERTY_KEY, entityType, Constants.STATE_PROPERTY_KEY, Id.EntityState.ACTIVE.name()); String guid = instanceVertex.getProperty(Constants.GUID_PROPERTY_KEY); return graphToInstanceMapper.mapGraphToTypedInstance(guid, instanceVertex); } @Override @GraphTransaction public List<String> getEntityList(String entityType) throws RepositoryException { LOG.debug("Retrieving entity list for type={}", entityType); GraphQuery query = titanGraph.query().has(Constants.ENTITY_TYPE_PROPERTY_KEY, entityType); Iterator<Vertex> results = query.vertices().iterator(); if (!results.hasNext()) { return Collections.emptyList(); } ArrayList<String> entityList = new ArrayList<>(); while (results.hasNext()) { Vertex vertex = results.next(); entityList.add(vertex.<String>getProperty(Constants.GUID_PROPERTY_KEY)); } return entityList; } /** * 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 @GraphTransaction public List<String> getTraitNames(String guid) throws AtlasException { LOG.debug("Retrieving trait names for entity={}", guid); Vertex instanceVertex = graphHelper.getVertexForGUID(guid); return GraphHelper.getTraitNames(instanceVertex); } /** * 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 @GraphTransaction public void addTrait(String guid, ITypedStruct traitInstance) throws RepositoryException { Preconditions.checkNotNull(traitInstance, "Trait instance cannot be null"); final String traitName = traitInstance.getTypeName(); LOG.debug("Adding a new trait={} for entity={}", traitName, guid); try { Vertex instanceVertex = graphHelper.getVertexForGUID(guid); // add the trait instance as a new vertex final String typeName = GraphHelper.getTypeName(instanceVertex); TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); instanceToGraphMapper.mapTraitInstanceToVertex( traitInstance, typeSystem.getDataType(ClassType.class, typeName), instanceVertex); // update the traits in entity once adding trait instance is successful GraphHelper.addProperty(instanceVertex, Constants.TRAIT_NAMES_PROPERTY_KEY, traitName); GraphHelper.setProperty( instanceVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime()); } catch (RepositoryException e) { throw e; } catch (Exception e) { 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 @GraphTransaction public void deleteTrait(String guid, String traitNameToBeDeleted) throws TraitNotFoundException, EntityNotFoundException, RepositoryException { LOG.debug("Deleting trait={} from entity={}", traitNameToBeDeleted, guid); Vertex instanceVertex = graphHelper.getVertexForGUID(guid); List<String> traitNames = GraphHelper.getTraitNames(instanceVertex); if (!traitNames.contains(traitNameToBeDeleted)) { throw new TraitNotFoundException( "Could not find trait=" + traitNameToBeDeleted + " in the repository for entity: " + guid); } try { final String entityTypeName = GraphHelper.getTypeName(instanceVertex); String relationshipLabel = GraphHelper.getTraitLabel(entityTypeName, traitNameToBeDeleted); Edge edge = GraphHelper.getEdgeForLabel(instanceVertex, relationshipLabel); deleteHandler.deleteEdgeReference(edge, DataTypes.TypeCategory.TRAIT, false, true); // update the traits in entity once trait removal is successful traitNames.remove(traitNameToBeDeleted); updateTraits(instanceVertex, traitNames); } catch (Exception e) { 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) { GraphHelper.addProperty(instanceVertex, Constants.TRAIT_NAMES_PROPERTY_KEY, traitName); } GraphHelper.setProperty( instanceVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, RequestContext.get().getRequestTime()); } @Override @GraphTransaction public AtlasClient.EntityResult updateEntities(ITypedReferenceableInstance... entitiesUpdated) throws RepositoryException { LOG.debug("updating entity {}", entitiesUpdated); try { TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); instanceToGraphMapper.mapTypedInstanceToGraph( TypedInstanceToGraphMapper.Operation.UPDATE_FULL, entitiesUpdated); RequestContext requestContext = RequestContext.get(); return new AtlasClient.EntityResult( requestContext.getCreatedEntityIds(), requestContext.getUpdatedEntityIds(), requestContext.getDeletedEntityIds()); } catch (AtlasException e) { throw new RepositoryException(e); } } @Override @GraphTransaction public AtlasClient.EntityResult updatePartial(ITypedReferenceableInstance entity) throws RepositoryException { LOG.debug("updating entity {}", entity); try { TypedInstanceToGraphMapper instanceToGraphMapper = new TypedInstanceToGraphMapper(graphToInstanceMapper, deleteHandler); instanceToGraphMapper.mapTypedInstanceToGraph( TypedInstanceToGraphMapper.Operation.UPDATE_PARTIAL, entity); RequestContext requestContext = RequestContext.get(); return new AtlasClient.EntityResult( requestContext.getCreatedEntityIds(), requestContext.getUpdatedEntityIds(), requestContext.getDeletedEntityIds()); } catch (AtlasException e) { throw new RepositoryException(e); } } @Override @GraphTransaction public AtlasClient.EntityResult deleteEntities(List<String> guids) throws RepositoryException { if (guids == null || guids.size() == 0) { throw new IllegalArgumentException("guids must be non-null and non-empty"); } for (String guid : guids) { if (guid == null) { LOG.warn("deleteEntities: Ignoring null guid"); continue; } try { Vertex instanceVertex = graphHelper.getVertexForGUID(guid); deleteHandler.deleteEntity(instanceVertex); } catch (EntityNotFoundException e) { // Entity does not exist - treat as non-error, since the caller // wanted to delete the entity and it's already gone. LOG.info("Deletion request ignored for non-existent entity with guid " + guid); continue; } catch (AtlasException e) { throw new RepositoryException(e); } } RequestContext requestContext = RequestContext.get(); return new AtlasClient.EntityResult( requestContext.getCreatedEntityIds(), requestContext.getUpdatedEntityIds(), requestContext.getDeletedEntityIds()); } }