Repository: incubator-s2graph Updated Branches: refs/heads/master 4f1c4ceb6 -> 35a369f55
add extension for SigmaJs Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/bb78c7d6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/bb78c7d6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/bb78c7d6 Branch: refs/heads/master Commit: bb78c7d602614ede86e93b465f0eafee50ee0554 Parents: 32eb344 Author: daewon <[email protected]> Authored: Fri Jun 8 16:58:21 2018 +0900 Committer: daewon <[email protected]> Committed: Fri Jun 8 16:58:21 2018 +0900 ---------------------------------------------------------------------- .../apache/s2graph/graphql/GraphQLServer.scala | 5 +- .../s2graph/graphql/middleware/SigmaJS.scala | 101 +++++++++++++++++ .../graphql/repository/GraphRepository.scala | 5 +- .../graphql/types/PlayJsonScalarType.scala | 112 +++++++++++++++++++ 4 files changed, 219 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/bb78c7d6/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala index eee7c93..d333c9e 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala @@ -28,6 +28,7 @@ import akka.http.scaladsl.server._ import com.typesafe.config.ConfigFactory import org.apache.s2graph.core.S2Graph import org.apache.s2graph.core.utils.SafeUpdateCache +import org.apache.s2graph.graphql.middleware.{SigmaJSFormatted} import org.apache.s2graph.graphql.repository.GraphRepository import org.apache.s2graph.graphql.types.SchemaDef import org.slf4j.LoggerFactory @@ -118,6 +119,7 @@ object GraphQLServer { private def executeGraphQLQuery(query: Document, op: Option[String], vars: JsObject)(implicit e: ExecutionContext) = { val cacheKey = className + "s2Schema" val s2schema = schemaCache.withCache(cacheKey, broadcast = false)(createNewSchema()) + import GraphRepository._ val resolver: DeferredResolver[GraphRepository] = DeferredResolver.fetchers(vertexFetcher, edgeFetcher) @@ -127,7 +129,8 @@ object GraphQLServer { s2Repository, variables = vars, operationName = op, - deferredResolver = resolver + deferredResolver = resolver, + middleware = SigmaJSFormatted :: Nil ) .map((res: spray.json.JsValue) => OK -> res) .recover { http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/bb78c7d6/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/SigmaJS.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/SigmaJS.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/SigmaJS.scala new file mode 100644 index 0000000..bf03ac2 --- /dev/null +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/SigmaJS.scala @@ -0,0 +1,101 @@ +package org.apache.s2graph.graphql.middleware + +import org.apache.s2graph.core.schema.ServiceColumn +import org.apache.s2graph.core.{GraphElement, S2EdgeLike, S2VertexLike} +import org.apache.s2graph.graphql.types.PlayJsonScalarType +import org.slf4s.LoggerFactory +import play.api.libs.json._ +import sangria.execution._ +import sangria.schema.Context + + +object SigmaJSFormatted extends Middleware[Any] with MiddlewareAfterField[Any] with MiddlewareExtension[Any] { + implicit val logger = LoggerFactory.getLogger(this.getClass) + + type QueryVal = java.util.concurrent.ConcurrentHashMap[GraphElement, Unit] + + type FieldVal = Long + + + def beforeQuery(context: MiddlewareQueryContext[Any, _, _]) = { + new java.util.concurrent.ConcurrentHashMap[GraphElement, Unit]() + } + + def afterQuery(queryVal: QueryVal, context: MiddlewareQueryContext[Any, _, _]) = () + + def toVertexId(v: S2VertexLike, c: ServiceColumn): String = { + val innerId = v.innerId.toIdString() + + s"${c.service.serviceName}.${c.columnName}.${innerId}" + } + + def toVertexJson(v: S2VertexLike, c: ServiceColumn): JsValue = { + Json.obj( + "id" -> toVertexId(v, c), + "label" -> v.innerId.toIdString() + ) + } + + def toEdgeJson(e: S2EdgeLike): JsValue = { + Json.obj( + "source" -> toVertexId(e.srcVertex, e.innerLabel.srcColumn), + "target" -> toVertexId(e.tgtVertex, e.innerLabel.tgtColumn), + "id" -> s"${toVertexId(e.srcVertex, e.innerLabel.srcColumn)}.${e.label()}.${toVertexId(e.tgtVertex, e.innerLabel.tgtColumn)}", + "label" -> e.label() + ) + } + + def afterQueryExtensions(queryVal: QueryVal, + context: MiddlewareQueryContext[Any, _, _] + ): Vector[Extension[_]] = { + + import scala.collection.JavaConverters._ + val elements = queryVal.keys().asScala.toVector + + val edges = elements.collect { case e: S2EdgeLike => e } + val vertices = elements.collect { case v: S2VertexLike => v -> v.serviceColumn } + val verticesFromEdges = edges.flatMap { e => + val label = e.innerLabel + Vector((e.srcVertex, label.srcColumn), (e.tgtVertex, label.srcColumn)) + } + + val verticesJson = (vertices ++ verticesFromEdges).distinct.map { case (v, c) => toVertexJson(v, c) } + val edgeJson = edges.distinct.map(toEdgeJson) + + val jsElements = Json.obj( + "nodes" -> verticesJson, + "edges" -> edgeJson + ) + + val graph = Json.obj("graph" -> jsElements) + + /** + * nodes: [{id, label, x, y, size}, ..], + * edges: [{id, source, target, label}] + */ + implicit val iu = PlayJsonScalarType.PlayJsonInputUnmarshaller + Vector(Extension[JsValue](graph)) + } + + def beforeField(queryVal: QueryVal, mctx: MiddlewareQueryContext[Any, _, _], ctx: Context[Any, _]) = { + continue(System.currentTimeMillis()) + } + + def afterField(queryVal: QueryVal, fieldVal: FieldVal, value: Any, mctx: MiddlewareQueryContext[Any, _, _], ctx: Context[Any, _]) = { + // logger.info(s"${ctx.parentType.name}.${ctx.field.name} = ${value.getClass.getName}") + + value match { + case ls: Seq[_] => ls.foreach { + case e: GraphElement => queryVal.put(e, ()) + case _ => + } + case e: GraphElement => queryVal.put(e, ()) + case _ => + } + + None + } +} + + + http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/bb78c7d6/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala index 0b5a2e9..b5e65dc 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/repository/GraphRepository.scala @@ -123,9 +123,8 @@ class GraphRepository(val graph: S2GraphLike) { } def getVertices(queryParam: VertexQueryParam): Future[Seq[S2VertexLike]] = { - graph.asInstanceOf[S2Graph].searchVertices(queryParam).map { a => - println(a) - a + graph.asInstanceOf[S2Graph].searchVertices(queryParam).map { v => + v } } http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/bb78c7d6/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala new file mode 100644 index 0000000..5149d36 --- /dev/null +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala @@ -0,0 +1,112 @@ +package org.apache.s2graph.graphql.types + +import play.api.libs.json._ +import sangria.ast +import sangria.execution.Executor +import sangria.marshalling.{ArrayMapBuilder, InputUnmarshaller, ResultMarshaller, ScalarValueInfo} +import sangria.schema._ +import sangria.validation.{BigIntCoercionViolation, IntCoercionViolation, ValueCoercionViolation} +import sangria.macros._ + +import scala.concurrent.ExecutionContext.Implicits.global + +object PlayJsonScalarType { + + implicit object CustomPlayJsonResultMarshaller extends ResultMarshaller { + type Node = JsValue + type MapBuilder = ArrayMapBuilder[Node] + + def emptyMapNode(keys: Seq[String]) = new ArrayMapBuilder[Node](keys) + + def addMapNodeElem(builder: MapBuilder, key: String, value: Node, optional: Boolean) = builder.add(key, value) + + def mapNode(builder: MapBuilder) = JsObject(builder.toMap) + + def mapNode(keyValues: Seq[(String, JsValue)]) = Json.toJson(keyValues.toMap) + + def arrayNode(values: Vector[JsValue]) = JsArray(values) + + def optionalArrayNodeValue(value: Option[JsValue]) = value match { + case Some(v) â v + case None â nullNode + } + + def scalarNode(value: Any, typeName: String, info: Set[ScalarValueInfo]) = value match { + case v: String â JsString(v) + case v: Boolean â JsBoolean(v) + case v: Int â JsNumber(v) + case v: Long â JsNumber(v) + case v: Float â JsNumber(BigDecimal(v)) + case v: Double â JsNumber(v) + case v: BigInt â JsNumber(BigDecimal(v)) + case v: BigDecimal â JsNumber(v) + case v: JsValue â v + case v â throw new IllegalArgumentException("Unsupported scalar value: " + v) + } + + def enumNode(value: String, typeName: String) = JsString(value) + + def nullNode = JsNull + + def renderCompact(node: JsValue) = Json.stringify(node) + + def renderPretty(node: JsValue) = Json.prettyPrint(node) + } + + implicit object PlayJsonInputUnmarshaller extends InputUnmarshaller[JsValue] { + def getRootMapValue(node: JsValue, key: String) = node.asInstanceOf[JsObject].value get key + + def isListNode(node: JsValue) = node.isInstanceOf[JsArray] + + def getListValue(node: JsValue) = node.asInstanceOf[JsArray].value + + def isMapNode(node: JsValue) = node.isInstanceOf[JsObject] + + def getMapValue(node: JsValue, key: String) = node.asInstanceOf[JsObject].value get key + + def getMapKeys(node: JsValue) = node.asInstanceOf[JsObject].fields.map(_._1) + + def isDefined(node: JsValue) = node != JsNull + + def getScalarValue(node: JsValue) = node match { + case JsBoolean(b) â b + case JsNumber(d) â d.toBigIntExact getOrElse d + case JsString(s) â s + case n â n + } + + def getScalaScalarValue(node: JsValue) = getScalarValue(node) + + def isEnumNode(node: JsValue) = node.isInstanceOf[JsString] + + def isScalarNode(node: JsValue) = true + + def isVariableNode(node: JsValue) = false + + def getVariableName(node: JsValue) = throw new IllegalArgumentException("variables are not supported") + + def render(node: JsValue) = Json.stringify(node) + } + + case object JsonCoercionViolation extends ValueCoercionViolation("Not valid JSON") + + implicit val JsonType = ScalarType[JsValue]("Json", + description = Some("Raw PlayJson value"), + coerceOutput = (value, _) â value, + coerceUserInput = { + case v: String â Right(JsString(v)) + case v: Boolean â Right(JsBoolean(v)) + case v: Int â Right(JsNumber(v)) + case v: Long â Right(JsNumber(v)) + case v: Float â Right(JsNumber(BigDecimal(v))) + case v: Double â Right(JsNumber(v)) + case v: BigInt â Right(JsNumber(BigDecimal(v))) + case v: BigDecimal â Right(JsNumber(v)) + case v: JsValue â Right(v) + }, + coerceInput = { + case sv: ast.StringValue => Right(Json.parse(sv.value)) + case _ â + Left(JsonCoercionViolation) + }) +}
