Skip to content

Commit

Permalink
✨ Can Process DiffGraphs (#68)
Browse files Browse the repository at this point in the history
* Vertex mapper can handle the case where ID isn't given

* Initial work on DiffGraph conversion

* Created initial diff graph processor

* Adjusted delete vertex for builders but ran into circular dependency

* Adjusted tests for new deleteVertex signature

* Added basic create and delete node

* Have tested DiffGraph operations

* Fixed ID bug in TigerGraphDriver.kt

* Documented new features and bumped version

* Added `updateVertexProperty` to IDriver and DiffGraphUtil.kt
  • Loading branch information
DavidBakerEffendi authored Feb 15, 2021
1 parent 7ee7f37 commit 464905c
Show file tree
Hide file tree
Showing 18 changed files with 365 additions and 90 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased] - yyyy-mm-dd

### Added
- `deleteEdge` to `IDriver`

### Changed

### Fixed

## [0.1.8] - 2021-02-15

### Added
- `deleteEdge` to `IDriver`
- `updateVertexProperty` to `IDriver`
- `DiffGraphUtil::processDiffGraph` to accept `DiffGraph`s and apply changes to a given `IDriver`

### Changed
- Modified `deleteVertex` signature to take ID and optional label

## [0.1.7] - 2021-02-11

### Changed
Expand Down
1 change: 0 additions & 1 deletion cpgconv/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ dependencies {
}

group = "io.github.plume-oss"
version = "0.0.3"

sourceSets {
main.java.srcDirs = []
Expand Down
2 changes: 1 addition & 1 deletion plume/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ dockerCompose {
def artifactDesc = "Plume is a code property graph analysis library with options to extract the CPG from" +
" Java bytecode and store the result in various graph databases."
def repoUrl = "https://github.com/plume-oss/plume.git"
def artifactVersion = "0.1.7"
def artifactVersion = "0.1.8"
def website = "https://plume-oss.github.io/plume-docs/"

group = "io.github.plume-oss"
Expand Down
2 changes: 1 addition & 1 deletion plume/src/main/kotlin/io/github/plume/oss/Extractor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ class Extractor(val driver: IDriver) {
}
}
logger.debug("Deleting $fileV")
driver.deleteVertex(mapToVertex(fileV) as NewFileBuilder)
driver.deleteVertex(fileV.id(), fileV.label())
} else {
logger.debug("Existing class was found and file hashes match, no need to rebuild.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import overflowdb.Node
import scala.Option
import scala.collection.immutable.`$colon$colon`
import scala.collection.immutable.`Nil$`
import scala.jdk.CollectionConverters
import java.util.*
import kotlin.collections.HashMap

Expand All @@ -26,6 +27,17 @@ object VertexMapper {
return mapToVertex(map)
}

/**
* Converts a [NewNode] to its respective [NewNodeBuilder] object.
*
* @param v The [NewNode] to deserialize.
* @return a [NewNodeBuilder] represented by the information in the givennode.
*/
fun mapToVertex(v: NewNode): NewNodeBuilder {
val map = CollectionConverters.MapHasAsJava(v.properties()).asJava() + mapOf<String, Any>("label" to v.label())
return mapToVertex(map)
}

/**
* Converts a [Map] containing vertex properties to its respective [NewNodeBuilder] object.
*
Expand All @@ -34,30 +46,26 @@ object VertexMapper {
*/
fun mapToVertex(mapToConvert: Map<String, Any>): NewNodeBuilder {
val map = HashMap<String, Any>()
// Only ID should be left as Long
mapToConvert.keys.forEach {
when (val value = mapToConvert[it]) {
is Long -> map[it] = value.toInt()
is Long -> if (it != "id") map[it] = value.toInt() else map[it] = value as Any
else -> map[it] = value as Any
}
}
map.computeIfPresent("id") { _, v -> v.toString().toLong() }
return when (map["label"] as String) {
ArrayInitializer.Label() -> NewArrayInitializerBuilder()
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
Binding.Label() -> NewBindingBuilder()
.name(map["NAME"] as String)
.signature(map["SIGNATURE"] as String)
.id(map["id"] as Long)
MetaData.Label() -> NewMetaDataBuilder()
.language(map["LANGUAGE"] as String)
.version(map["VERSION"] as String)
.id(map["id"] as Long)
File.Label() -> NewFileBuilder()
.name(map["NAME"] as String)
.hash(Option.apply(map["HASH"] as String))
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
Method.Label() -> NewMethodBuilder()
.astParentFullName(map["AST_PARENT_FULL_NAME"] as String)
.astParentType(map["AST_PARENT_TYPE"] as String)
Expand All @@ -68,7 +76,6 @@ object VertexMapper {
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
MethodParameterIn.Label() -> NewMethodParameterInBuilder()
.code(map["CODE"] as String)
.name(map["NAME"] as String)
Expand All @@ -77,59 +84,49 @@ object VertexMapper {
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
MethodReturn.Label() -> NewMethodReturnBuilder()
.code(map["CODE"] as String)
.evaluationStrategy(map["EVALUATION_STRATEGY"] as String)
.typeFullName(map["TYPE_FULL_NAME"] as String)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
Modifier.Label() -> NewModifierBuilder()
.modifierType(map["MODIFIER_TYPE"] as String)
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
Type.Label() -> NewTypeBuilder()
.name(map["NAME"] as String)
.fullName(map["FULL_NAME"] as String)
.typeDeclFullName(map["TYPE_DECL_FULL_NAME"] as String)
.id(map["id"] as Long)
TypeDecl.Label() -> NewTypeDeclBuilder()
.astParentFullName(map["AST_PARENT_FULL_NAME"] as String)
.astParentType(map["AST_PARENT_TYPE"] as String)
.name(map["NAME"] as String)
.fullName(map["FULL_NAME"] as String)
.order(map["ORDER"] as Int)
.isExternal(map["IS_EXTERNAL"] as Boolean)
.id(map["id"] as Long)
TypeParameter.Label() -> NewTypeParameterBuilder()
.name(map["NAME"] as String)
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
TypeArgument.Label() -> NewTypeArgumentBuilder()
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
Member.Label() -> NewMemberBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.code(map["CODE"] as String)
.name(map["NAME"] as String)
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
NamespaceBlock.Label() -> NewNamespaceBlockBuilder()
.fullName(map["FULL_NAME"] as String)
.filename(map["FILENAME"] as String)
.name(map["NAME"] as String)
.order(map["ORDER"] as Int)
.id(map["id"] as Long)
Literal.Label() -> NewLiteralBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
Call.Label() -> NewCallBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.code(map["CODE"] as String)
Expand All @@ -148,15 +145,13 @@ object VertexMapper {
else -> createScalaList("")
}
)
.id(map["id"] as Long)
Local.Label() -> NewLocalBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.code(map["CODE"] as String)
.name(map["NAME"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.id(map["id"] as Long)
Identifier.Label() -> NewIdentifierBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.code(map["CODE"] as String)
Expand All @@ -165,30 +160,26 @@ object VertexMapper {
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
FieldIdentifier.Label() -> NewFieldIdentifierBuilder()
.canonicalName(map["CANONICAL_NAME"] as String)
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
Return.Label() -> NewReturnBuilder()
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
Block.Label() -> NewBlockBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
MethodRef.Label() -> NewMethodRefBuilder()
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
Expand All @@ -197,7 +188,6 @@ object VertexMapper {
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
TypeRef.Label() -> NewTypeRefBuilder()
.typeFullName(map["TYPE_FULL_NAME"] as String)
.dynamicTypeHintFullName(
Expand All @@ -212,31 +202,27 @@ object VertexMapper {
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
JumpTarget.Label() -> NewJumpTargetBuilder()
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.name(map["NAME"] as String)
.id(map["id"] as Long)
ControlStructure.Label() -> NewControlStructureBuilder()
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.id(map["id"] as Long)
else -> NewUnknownBuilder()
.code(map["CODE"] as String)
.order(map["ORDER"] as Int)
.argumentIndex(map["ARGUMENT_INDEX"] as Int)
.typeFullName(map["TYPE_FULL_NAME"] as String)
.lineNumber(Option.apply(map["LINE_NUMBER"] as Int))
.columnNumber(Option.apply(map["COLUMN_NUMBER"] as Int))
.id(map["id"] as Long)
}
}.apply { if (map.containsKey("id")) this.id(map["id"] as Long) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ import org.apache.logging.log4j.LogManager
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions
import org.apache.tinkerpop.gremlin.structure.Edge
import org.apache.tinkerpop.gremlin.structure.Graph
import org.apache.tinkerpop.gremlin.structure.T
import org.apache.tinkerpop.gremlin.structure.Vertex
import org.apache.tinkerpop.gremlin.structure.*
import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph
import overflowdb.Config
import scala.jdk.CollectionConverters
Expand Down Expand Up @@ -202,9 +199,9 @@ abstract class GremlinDriver : IDriver {
return gremlinToPlume(neighbourSubgraph.traversal())
}

override fun deleteVertex(v: NewNodeBuilder) {
if (!exists(v)) return
findVertexTraversal(v).drop().iterate()
override fun deleteVertex(id: Long, label: String?) {
if (!g.V(id).hasNext()) return
g.V(id).drop().iterate()
}

override fun deleteEdge(src: NewNodeBuilder, tgt: NewNodeBuilder, edge: String) {
Expand All @@ -226,6 +223,11 @@ abstract class GremlinDriver : IDriver {
}
}

override fun updateVertexProperty(id: Long, label: String?, key: String, value: Any) {
if (!g.V(id).hasNext()) return
g.V(id).property(key, value).iterate()
}

/**
* Converts a [GraphTraversalSource] instance to a [overflowdb.Graph] instance.
*
Expand Down
15 changes: 13 additions & 2 deletions plume/src/main/kotlin/io/github/plume/oss/drivers/IDriver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ interface IDriver : AutoCloseable {
/**
* Given a vertex, will remove it from the graph if present.
*
* @param v The vertex to remove.
* @param id The id to remove.
* @param label The label, if known.
*/
fun deleteVertex(v: NewNodeBuilder)
fun deleteVertex(id: Long, label: String? = null)

/**
* Given the full signature of a method, removes the method and its body from the graph.
Expand All @@ -112,4 +113,14 @@ interface IDriver : AutoCloseable {
*/
fun deleteEdge(src: NewNodeBuilder, tgt: NewNodeBuilder, edge: String)

/**
* Updates a vertex's property if the node exists.
*
* @param id The ID of the vertex to update.
* @param label The label of the node, if known.
* @param key The key of the property to update.
* @param value The updated value.
*/
fun updateVertexProperty(id: Long, label: String?, key: String, value: Any)

}
32 changes: 24 additions & 8 deletions plume/src/main/kotlin/io/github/plume/oss/drivers/Neo4jDriver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,15 @@ class Neo4jDriver : IDriver {
}
}

override fun exists(v: NewNodeBuilder): Boolean {
val node = v.build()
override fun exists(v: NewNodeBuilder): Boolean = checkVertexExist(v.id(), v.build().label())

private fun checkVertexExist(id: Long, label: String? = null): Boolean {
driver.session().use { session ->
return session.writeTransaction { tx ->
val result = tx.run(
"""
MATCH (n:${node.label()})
WHERE id(n) = ${v.id()}
MATCH (n${if (label != null) ":$label" else ""})
WHERE id(n) = $id
RETURN n
""".trimIndent()
)
Expand Down Expand Up @@ -374,14 +375,14 @@ class Neo4jDriver : IDriver {
}
}

override fun deleteVertex(v: NewNodeBuilder) {
if (!exists(v)) return
override fun deleteVertex(id: Long, label: String?) {
if (!checkVertexExist(id, label)) return
driver.session().use { session ->
session.writeTransaction { tx ->
tx.run(
"""
MATCH (n)
WHERE ID(n) = ${v.id()}
MATCH (n${if (label != null) ":$label" else ""})
WHERE ID(n) = $id
DETACH DELETE n
""".trimIndent()
)
Expand Down Expand Up @@ -421,6 +422,21 @@ class Neo4jDriver : IDriver {
}
}

override fun updateVertexProperty(id: Long, label: String?, key: String, value: Any) {
if (!checkVertexExist(id, label)) return
driver.session().use { session ->
session.writeTransaction { tx ->
tx.run(
"""
MATCH (n${if (label != null) ":$label" else ""})
WHERE ID(n) = $id
SET n.$key = ${if (value is String) "\"$value\"" else value}
""".trimIndent()
)
}
}
}

private fun newOverflowGraph(): Graph = Graph.open(
Config.withDefaults(),
NodeFactories.allAsJava(),
Expand Down
Loading

0 comments on commit 464905c

Please sign in to comment.