Changing query more intuitively by using `columnName` instead of `from/to` in label field name
Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/da15232c Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/da15232c Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/da15232c Branch: refs/heads/master Commit: da15232cf151a89184f9be498db31e2f3877edf8 Parents: afc22d7 Author: daewon <[email protected]> Authored: Tue Apr 3 15:27:58 2018 +0900 Committer: daewon <[email protected]> Committed: Wed Apr 4 14:43:05 2018 +0900 ---------------------------------------------------------------------- .../apache/s2graph/graphql/GraphQLServer.scala | 1 + .../apache/s2graph/graphql/bind/AstHelper.scala | 9 + .../s2graph/graphql/bind/Unmarshaller.scala | 159 +++++++++ .../s2graph/graphql/marshaller/package.scala | 124 ------- .../graphql/repository/GraphRepository.scala | 5 - .../s2graph/graphql/resolver/Resolver.scala | 28 -- .../s2graph/graphql/types/ManagementType.scala | 312 +++++++++++++++++ .../graphql/types/S2ManagementType.scala | 339 ------------------- .../apache/s2graph/graphql/types/S2Type.scala | 143 ++++---- .../types/SangriaPlayJsonScalarType.scala | 76 ----- .../s2graph/graphql/types/SchemaDef.scala | 4 +- .../s2graph/graphql/types/StaticType.scala | 149 ++++++++ .../apache/s2graph/graphql/types/package.scala | 137 +------- .../apache/s2graph/graphql/ScenarioTest.scala | 52 +-- .../org/apache/s2graph/graphql/SchemaTest.scala | 12 +- .../org/apache/s2graph/graphql/TestGraph.scala | 1 + 16 files changed, 735 insertions(+), 816 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/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 e4b43cc..dcddd01 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala @@ -29,6 +29,7 @@ import com.typesafe.config.ConfigFactory import org.apache.s2graph.core.S2Graph import org.apache.s2graph.core.utils.SafeUpdateCache import org.apache.s2graph.graphql.repository.GraphRepository +import org.apache.s2graph.graphql.types.SchemaDef import org.slf4j.LoggerFactory import sangria.ast.Document import sangria.execution._ http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/AstHelper.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/AstHelper.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/AstHelper.scala new file mode 100644 index 0000000..ec1c04c --- /dev/null +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/AstHelper.scala @@ -0,0 +1,9 @@ +package org.apache.s2graph.graphql.bind + +object AstHelper { + def selectedFields(astFields: Seq[sangria.ast.Field]): Vector[String] = { + astFields.flatMap { f => + f.selections.map(s => s.asInstanceOf[sangria.ast.Field].name) + }.toVector + } +} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/Unmarshaller.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/Unmarshaller.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/Unmarshaller.scala new file mode 100644 index 0000000..f7a360a --- /dev/null +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/bind/Unmarshaller.scala @@ -0,0 +1,159 @@ +/* + * 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.s2graph.graphql.bind + +import org.apache.s2graph.core.Management.JsonModel._ +import org.apache.s2graph.core.mysqls.ServiceColumn +import org.apache.s2graph.core.{S2EdgeLike, S2VertexLike} +import org.apache.s2graph.graphql.repository.GraphRepository +import org.apache.s2graph.graphql.types.S2Type._ +import sangria.marshalling._ +import sangria.schema.Context + +object Unmarshaller { + type RawNode = Map[String, Any] + + def unwrap(any: Any): Any = any match { + case s: Some[_] => unwrap(s.get) + case v: Seq[_] => v.map(unwrap) + case m: Map[_, _] => m.mapValues(unwrap) + case _ => any + } + + implicit object AddVertexParamFromInput extends FromInput[List[AddVertexParam]] { + val marshaller = CoercedScalaResultMarshaller.default + + def fromResult(node: marshaller.Node) = { + val now = System.currentTimeMillis() + val map = unwrap(node).asInstanceOf[RawNode] + + val params = map.flatMap { case (columnName, vls: Vector[_]) => + vls.map { _m => + val m = _m.asInstanceOf[RawNode] + val id = m("id") + val ts = m.getOrElse("timestamp", now).asInstanceOf[Long] + + AddVertexParam(ts, id, columnName, props = m) + } + } + + params.toList + } + } + + implicit object AddEdgeParamFromInput extends FromInput[AddEdgeParam] { + val marshaller = CoercedScalaResultMarshaller.default + + def fromResult(node: marshaller.Node) = { + val inputMap = unwrap(node).asInstanceOf[RawNode] + val now = System.currentTimeMillis() + + val from = inputMap("from") + val to = inputMap("to") + val ts = inputMap.get("timestamp").map(_.asInstanceOf[Long]).getOrElse(now) + val dir = inputMap.get("direction").map(_.asInstanceOf[String]).getOrElse("out") + val props = inputMap + + AddEdgeParam(ts, from, to, dir, props) + } + } + + implicit object IndexFromInput extends FromInput[Index] { + val marshaller = CoercedScalaResultMarshaller.default + + def fromResult(node: marshaller.Node) = { + val input = node.asInstanceOf[RawNode] + Index(input("name").asInstanceOf[String], input("propNames").asInstanceOf[Seq[String]]) + } + } + + implicit object PropFromInput extends FromInput[Prop] { + val marshaller = CoercedScalaResultMarshaller.default + + def fromResult(node: marshaller.Node) = { + val input = node.asInstanceOf[RawNode] + + val name = input("name").asInstanceOf[String] + val defaultValue = input("defaultValue").asInstanceOf[String] + val dataType = input("dataType").asInstanceOf[String] + val storeInGlobalIndex = input("storeInGlobalIndex").asInstanceOf[Boolean] + + Prop(name, defaultValue, dataType, storeInGlobalIndex) + } + } + + implicit object ServiceColumnParamFromInput extends FromInput[ServiceColumnParam] { + val marshaller = CoercedScalaResultMarshaller.default + + def fromResult(node: marshaller.Node) = ServiceColumnParamsFromInput.fromResult(node).head + } + + implicit object ServiceColumnParamsFromInput extends FromInput[Vector[ServiceColumnParam]] { + val marshaller = CoercedScalaResultMarshaller.default + + def fromResult(node: marshaller.Node) = { + val input = unwrap(node.asInstanceOf[Map[String, Any]]).asInstanceOf[Map[String, Any]] + + val partialServiceColumns = input.map { case (serviceName, serviceColumnMap) => + val innerMap = serviceColumnMap.asInstanceOf[Map[String, Any]] + val columnName = innerMap("columnName").asInstanceOf[String] + val props = innerMap.get("props").toSeq.flatMap { case vs: Vector[_] => + vs.map(PropFromInput.fromResult) + } + + ServiceColumnParam(serviceName, columnName, props) + } + + partialServiceColumns.toVector + } + } + + def labelField(c: Context[GraphRepository, Any]): (S2VertexLike, String) = { + val vertex = c.value.asInstanceOf[S2VertexLike] + val dir = c.arg[String]("direction") + + (vertex, dir) + } + + def serviceColumnFieldOnService(column: ServiceColumn, c: Context[GraphRepository, Any]): (Seq[S2VertexLike], Boolean) = { + val ids = c.argOpt[Any]("id").toSeq ++ c.argOpt[List[Any]]("ids").toList.flatten + val vertices = ids.map(vid => c.ctx.toS2VertexLike(vid, column)) + + val columnFields = column.metasInvMap.keySet + val selectedFields = AstHelper.selectedFields(c.astFields) + + val canSkipFetch = selectedFields.forall(f => f == "id" || !columnFields(f)) + + (vertices, canSkipFetch) + } + + def serviceColumnFieldOnLabel(column: ServiceColumn, c: Context[GraphRepository, Any]): (S2VertexLike, Boolean) = { + val edge = c.value.asInstanceOf[S2EdgeLike] + val vertex = if (edge.getDirection() == "in") edge.srcForVertex else edge.tgtForVertex + + val selectedFields = AstHelper.selectedFields(c.astFields) + val columnFields = column.metasInvMap.keySet + + val canSkipFetch = selectedFields.forall(f => f == "id" || !columnFields(f)) + + (vertex, canSkipFetch) + } + +} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala deleted file mode 100644 index 3b675b3..0000000 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/marshaller/package.scala +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.s2graph.graphql - -import org.apache.s2graph.core.Management.JsonModel._ -import org.apache.s2graph.graphql.types.S2Type._ -import sangria.marshalling._ -import sangria.schema.Args - -package object marshaller { - type RawNode = Map[String, Any] - - def unwrap(any: Any): Any = any match { - case s: Some[_] => unwrap(s.get) - case v: Seq[_] => v.map(unwrap) - case m: Map[_, _] => m.mapValues(unwrap) - case _ => any - } - - implicit object AddVertexParamFromInput extends FromInput[List[AddVertexParam]] { - val marshaller = CoercedScalaResultMarshaller.default - - def fromResult(node: marshaller.Node) = { - val now = System.currentTimeMillis() - val map = unwrap(node).asInstanceOf[RawNode] - - val params = map.flatMap { case (columnName, vls: Vector[_]) => - vls.map { _m => - val m = _m.asInstanceOf[RawNode] - val id = m("id") - val ts = m.getOrElse("timestamp", now).asInstanceOf[Long] - - AddVertexParam(ts, id, columnName, props = m) - } - } - - params.toList - } - } - - implicit object AddEdgeParamFromInput extends FromInput[AddEdgeParam] { - val marshaller = CoercedScalaResultMarshaller.default - - def fromResult(node: marshaller.Node) = { - val inputMap = unwrap(node).asInstanceOf[RawNode] - val now = System.currentTimeMillis() - - val from = inputMap("from") - val to = inputMap("to") - val ts = inputMap.get("timestamp").map(_.asInstanceOf[Long]).getOrElse(now) - val dir = inputMap.get("direction").map(_.asInstanceOf[String]).getOrElse("out") - val props = inputMap - - AddEdgeParam(ts, from, to, dir, props) - } - } - - implicit object IndexFromInput extends FromInput[Index] { - val marshaller = CoercedScalaResultMarshaller.default - - def fromResult(node: marshaller.Node) = { - val input = node.asInstanceOf[RawNode] - Index(input("name").asInstanceOf[String], input("propNames").asInstanceOf[Seq[String]]) - } - } - - implicit object PropFromInput extends FromInput[Prop] { - val marshaller = CoercedScalaResultMarshaller.default - - def fromResult(node: marshaller.Node) = { - val input = node.asInstanceOf[RawNode] - - val name = input("name").asInstanceOf[String] - val defaultValue = input("defaultValue").asInstanceOf[String] - val dataType = input("dataType").asInstanceOf[String] - val storeInGlobalIndex = input("storeInGlobalIndex").asInstanceOf[Boolean] - - Prop(name, defaultValue, dataType, storeInGlobalIndex) - } - } - - implicit object ServiceColumnParamFromInput extends FromInput[ServiceColumnParam] { - val marshaller = CoercedScalaResultMarshaller.default - - def fromResult(node: marshaller.Node) = ServiceColumnParamsFromInput.fromResult(node).head - } - - implicit object ServiceColumnParamsFromInput extends FromInput[Vector[ServiceColumnParam]] { - val marshaller = CoercedScalaResultMarshaller.default - - def fromResult(node: marshaller.Node) = { - val input = unwrap(node.asInstanceOf[Map[String, Any]]).asInstanceOf[Map[String, Any]] - - val partialServiceColumns = input.map { case (serviceName, serviceColumnMap) => - val innerMap = serviceColumnMap.asInstanceOf[Map[String, Any]] - val columnName = innerMap("columnName").asInstanceOf[String] - val props = innerMap.get("props").toSeq.flatMap { case vs: Vector[_] => - vs.map(PropFromInput.fromResult) - } - - ServiceColumnParam(serviceName, columnName, props) - } - - partialServiceColumns.toVector - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/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 a29d0fb..ca06bc8 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 @@ -42,13 +42,8 @@ object GraphRepository { tryObj } - } -/** - * - * @param graph - */ class GraphRepository(val graph: S2GraphLike) { implicit val logger = LoggerFactory.getLogger(this.getClass) http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala deleted file mode 100644 index 1a96d84..0000000 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/resolver/Resolver.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.s2graph.graphql.resolver - -import org.apache.s2graph.core.S2VertexLike -import org.apache.s2graph.graphql.repository.GraphRepository - -object Resolver { - def vertexResolver(v: S2VertexLike)(implicit repo: GraphRepository) { - } -} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala new file mode 100644 index 0000000..d07feeb --- /dev/null +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala @@ -0,0 +1,312 @@ +/* + * 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.s2graph.graphql.types + +import org.apache.s2graph.core.mysqls._ +import org.apache.s2graph.graphql.repository.GraphRepository +import sangria.schema._ + +import scala.language.existentials +import scala.util.{Failure, Success, Try} +import org.apache.s2graph.graphql.types.S2Type.{ServiceColumnParam} + +object ManagementType { + + import sangria.schema._ + + case class MutationResponse[T](result: Try[T]) + + def makeMutationResponseType[T](name: String, desc: String, tpe: ObjectType[_, T]): ObjectType[Unit, MutationResponse[T]] = { + val retType = ObjectType( + name, + desc, + () => fields[Unit, MutationResponse[T]]( + Field("isSuccess", + BooleanType, + resolve = _.value.result.isSuccess + ), + Field("message", + StringType, + resolve = _.value.result match { + case Success(_) => s"Mutation successful" + case Failure(ex) => ex.getMessage + } + ), + Field("object", + OptionType(tpe), + resolve = _.value.result.toOption + ) + ) + ) + + retType + } +} + +class ManagementType(repo: GraphRepository) { + + import ManagementType._ + import sangria.macros.derive._ + import org.apache.s2graph.graphql.bind.Unmarshaller._ + import org.apache.s2graph.graphql.types.StaticTypes._ + + lazy val serviceColumnOnServiceWithPropInputObjectFields = repo.allServices.map { service => + InputField(service.serviceName, OptionInputType(InputObjectType( + s"Input_${service.serviceName}_ServiceColumn_Props", + description = "desc here", + fields = List( + InputField("columnName", makeServiceColumnEnumTypeOnService(service)), + InputField("props", ListInputType(InputPropType)) + ) + ))) + } + + lazy val serviceColumnOnServiceInputObjectFields = repo.allServices.map { service => + InputField(service.serviceName, OptionInputType(InputObjectType( + s"Input_${service.serviceName}_ServiceColumn", + description = "desc here", + fields = List( + InputField("columnName", makeServiceColumnEnumTypeOnService(service)) + ) + ))) + } + + def makeServiceColumnEnumTypeOnService(service: Service): EnumType[String] = { + val columns = service.serviceColumns(false).toList + EnumType( + s"Enum_${service.serviceName}_ServiceColumn", + description = Option("desc here"), + values = dummyEnum +: columns.map { column => + EnumValue(column.columnName, value = column.columnName) + } + ) + } + + lazy val labelPropsInputFields = repo.allLabels().map { label => + InputField(label.label, OptionInputType(InputObjectType( + s"Input_${label.label}_props", + description = "desc here", + fields = List( + InputField("props", ListInputType(InputPropType)) + ) + ))) + } + + lazy val ServiceType = deriveObjectType[GraphRepository, Service]( + ObjectTypeName("Service"), + ObjectTypeDescription("desc here"), + RenameField("serviceName", "name"), + AddFields( + Field("serviceColumns", ListType(ServiceColumnType), resolve = c => c.value.serviceColumns(false).toList) + ) + ) + + lazy val ServiceColumnType = deriveObjectType[GraphRepository, ServiceColumn]( + ObjectTypeName("ServiceColumn"), + ObjectTypeDescription("desc here"), + RenameField("columnName", "name"), + AddFields( + Field("props", ListType(ColumnMetaType), + resolve = c => c.value.metasWithoutCache.filter(ColumnMeta.isValid) + ) + ) + ) + + val dummyEnum = EnumValue("_", value = "_") + + lazy val ServiceListType = EnumType( + s"Enum_Service", + description = Option("desc here"), + values = + dummyEnum +: repo.allServices.map { service => + EnumValue(service.serviceName, value = service.serviceName) + } + ) + + lazy val ServiceColumnListType = EnumType( + s"Enum_ServiceColumn", + description = Option("desc here"), + values = + dummyEnum +: repo.allServiceColumns.map { serviceColumn => + EnumValue(serviceColumn.columnName, value = serviceColumn.columnName) + } + ) + + lazy val EnumLabelsType = EnumType( + s"Enum_Label", + description = Option("desc here"), + values = + dummyEnum +: repo.allLabels().map { label => + EnumValue(label.label, value = label.label) + } + ) + + lazy val ServiceMutationResponseType = makeMutationResponseType[Service]( + "MutateService", + "desc here", + ServiceType + ) + + lazy val ServiceColumnMutationResponseType = makeMutationResponseType[ServiceColumn]( + "MutateServiceColumn", + "desc here", + ServiceColumnType + ) + + lazy val LabelMutationResponseType = makeMutationResponseType[Label]( + "MutateLabel", + "desc here", + LabelType + ) + + lazy val labelsField: Field[GraphRepository, Any] = Field( + "Labels", + ListType(LabelType), + description = Option("desc here"), + arguments = List(LabelNameArg), + resolve = { c => + c.argOpt[String]("name") match { + case Some(name) => c.ctx.allLabels().filter(_.label == name) + case None => c.ctx.allLabels() + } + } + ) + + val serviceOptArgs = List( + "compressionAlgorithm" -> CompressionAlgorithmType, + "cluster" -> StringType, + "hTableName" -> StringType, + "preSplitSize" -> IntType, + "hTableTTL" -> IntType + ).map { case (name, _type) => Argument(name, OptionInputType(_type)) } + + val AddPropServiceType = InputObjectType[ServiceColumnParam]( + "Input_Service_ServiceColumn_Props", + description = "desc", + fields = DummyInputField +: serviceColumnOnServiceWithPropInputObjectFields + ) + + val ServiceColumnSelectType = InputObjectType[ServiceColumnParam]( + "Input_Service_ServiceColumn", + description = "desc", + fields = DummyInputField +: serviceColumnOnServiceInputObjectFields + ) + + val InputServiceType = InputObjectType[ServiceColumnParam]( + "Input_Service", + description = "desc", + fields = DummyInputField +: serviceColumnOnServiceInputObjectFields + ) + + lazy val servicesField: Field[GraphRepository, Any] = Field( + "Services", + ListType(ServiceType), + description = Option("desc here"), + arguments = List(ServiceNameArg), + resolve = { c => + c.argOpt[String]("name") match { + case Some(name) => c.ctx.allServices.filter(_.serviceName == name) + case None => c.ctx.allServices + } + } + ) + + /** + * Query Fields + * Provide s2graph management query API + */ + lazy val queryFields: List[Field[GraphRepository, Any]] = List(servicesField, labelsField) + + /** + * Mutation fields + * Provide s2graph management mutate API + * + * - createService + * - createLabel + * - ... + */ + + lazy val labelRequiredArg = List( + Argument("sourceService", InputServiceType), + Argument("targetService", InputServiceType) + ) + + val labelOptsArgs = List( + Argument("serviceName", OptionInputType(ServiceListType)), + Argument("consistencyLevel", OptionInputType(ConsistencyLevelType)), + Argument("isDirected", OptionInputType(BooleanType)), + Argument("isAsync", OptionInputType(BooleanType)), + Argument("schemaVersion", OptionInputType(StringType)) + ) + + val NameArg = Argument("name", StringType, description = "desc here") + + lazy val ServiceNameArg = Argument("name", OptionInputType(ServiceListType), description = "desc here") + + lazy val ServiceNameRawArg = Argument("serviceName", ServiceListType, description = "desc here") + + lazy val ColumnNameArg = Argument("columnName", OptionInputType(ServiceColumnListType), description = "desc here") + + lazy val ColumnTypeArg = Argument("columnType", DataTypeType, description = "desc here") + + lazy val LabelNameArg = Argument("name", OptionInputType(EnumLabelsType), description = "desc here") + + lazy val PropArg = Argument("props", OptionInputType(ListInputType(InputPropType)), description = "desc here") + + lazy val IndicesArg = Argument("indices", OptionInputType(ListInputType(InputIndexType)), description = "desc here") + + lazy val mutationFields: List[Field[GraphRepository, Any]] = List( + Field("createService", + ServiceMutationResponseType, + arguments = NameArg :: serviceOptArgs, + resolve = c => MutationResponse(c.ctx.createService(c.args)) + ), + Field("createLabel", + LabelMutationResponseType, + arguments = NameArg :: PropArg :: IndicesArg :: labelRequiredArg ::: labelOptsArgs, + resolve = c => MutationResponse(c.ctx.createLabel(c.args)) + ), + Field("deleteLabel", + LabelMutationResponseType, + arguments = LabelNameArg :: Nil, + resolve = c => MutationResponse(c.ctx.deleteLabel(c.args)) + ), + Field("createServiceColumn", + ServiceColumnMutationResponseType, + arguments = List(ServiceNameRawArg, Argument("columnName", StringType), ColumnTypeArg, PropArg), + resolve = c => MutationResponse(c.ctx.createServiceColumn(c.args)) + ), + Field("deleteServiceColumn", + ServiceColumnMutationResponseType, + arguments = Argument("service", ServiceColumnSelectType) :: Nil, + resolve = c => MutationResponse(c.ctx.deleteServiceColumn(c.args)) + ), + Field("addPropsToServiceColumn", + ServiceColumnMutationResponseType, + arguments = Argument("service", AddPropServiceType) :: Nil, + resolve = c => MutationResponse(c.ctx.addPropsToServiceColumn(c.args)) + ), + Field("addPropsToLabel", + LabelMutationResponseType, + arguments = Argument("labelName", EnumLabelsType) :: PropArg :: Nil, + resolve = c => MutationResponse(c.ctx.addPropsToLabel(c.args)) + ) + ) +} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2ManagementType.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2ManagementType.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2ManagementType.scala deleted file mode 100644 index 18df00b..0000000 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2ManagementType.scala +++ /dev/null @@ -1,339 +0,0 @@ -/* - * 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.s2graph.graphql.types - -import org.apache.s2graph.core.Management.JsonModel._ -import org.apache.s2graph.core._ -import org.apache.s2graph.core.mysqls._ -import org.apache.s2graph.core.storage.MutateResponse -import org.apache.s2graph.graphql._ -import org.apache.s2graph.graphql.repository.GraphRepository -import play.api.libs.json.JsValue -import sangria.marshalling._ -import sangria.schema._ - -import scala.language.existentials -import scala.util.{Failure, Success, Try} -import org.apache.s2graph.graphql.marshaller._ -import org.apache.s2graph.graphql.types.S2Type.{ServiceColumnParam} - -object S2ManagementType { - - import sangria.schema._ - - case class MutationResponse[T](result: Try[T]) - - def makeMutationResponseType[T](name: String, desc: String, tpe: ObjectType[_, T]): ObjectType[Unit, MutationResponse[T]] = { - val retType = ObjectType( - name, - desc, - () => fields[Unit, MutationResponse[T]]( - Field("isSuccess", - BooleanType, - resolve = _.value.result.isSuccess - ), - Field("message", - StringType, - resolve = _.value.result match { - case Success(_) => s"Mutation successful" - case Failure(ex) => ex.getMessage - } - ), - Field("object", - OptionType(tpe), - resolve = _.value.result.toOption - ) - ) - ) - - retType - } -} - -class S2ManagementType(repo: GraphRepository) { - - import S2ManagementType._ - - import sangria.macros.derive._ - - lazy val serviceColumnOnServiceWithPropInputObjectFields = repo.allServices.map { service => - InputField(service.serviceName, OptionInputType(InputObjectType( - s"Input_${service.serviceName}_ServiceColumn_Props", - description = "desc here", - fields = List( - InputField("columnName", makeServiceColumnEnumTypeOnService(service)), - InputField("props", ListInputType(InputPropType)) - ) - ))) - } - - lazy val serviceColumnOnServiceInputObjectFields = repo.allServices.map { service => - InputField(service.serviceName, OptionInputType(InputObjectType( - s"Input_${service.serviceName}_ServiceColumn", - description = "desc here", - fields = List( - InputField("columnName", makeServiceColumnEnumTypeOnService(service)) - ) - ))) - } - - def makeServiceColumnEnumTypeOnService(service: Service): EnumType[String] = { - val columns = service.serviceColumns(false).toList - EnumType( - s"Enum_${service.serviceName}_ServiceColumn", - description = Option("desc here"), - values = dummyEnum +: columns.map { column => - EnumValue(column.columnName, value = column.columnName) - } - ) - } - - lazy val labelPropsInputFields = repo.allLabels().map { label => - InputField(label.label, OptionInputType(InputObjectType( - s"Input_${label.label}_props", - description = "desc here", - fields = List( - InputField("props", ListInputType(InputPropType)) - ) - ))) - } - - lazy val ServiceType = deriveObjectType[GraphRepository, Service]( - ObjectTypeName("Service"), - ObjectTypeDescription("desc here"), - RenameField("serviceName", "name"), - AddFields( - Field("serviceColumns", ListType(ServiceColumnType), resolve = c => c.value.serviceColumns(false).toList) - ) - ) - - lazy val ServiceColumnType = deriveObjectType[GraphRepository, ServiceColumn]( - ObjectTypeName("ServiceColumn"), - ObjectTypeDescription("desc here"), - RenameField("columnName", "name"), - AddFields( - Field("props", ListType(ColumnMetaType), - resolve = c => c.value.metasWithoutCache.filter(ColumnMeta.isValid) - ) - ) - ) - - val dummyEnum = EnumValue("_", value = "_") - - lazy val ServiceListType = EnumType( - s"Enum_Service", - description = Option("desc here"), - values = - dummyEnum +: repo.allServices.map { service => - EnumValue(service.serviceName, value = service.serviceName) - } - ) - - lazy val ServiceColumnListType = EnumType( - s"Enum_ServiceColumn", - description = Option("desc here"), - values = - dummyEnum +: repo.allServiceColumns.map { serviceColumn => - EnumValue(serviceColumn.columnName, value = serviceColumn.columnName) - } - ) - - lazy val EnumLabelsType = EnumType( - s"Enum_Label", - description = Option("desc here"), - values = - dummyEnum +: repo.allLabels().map { label => - EnumValue(label.label, value = label.label) - } - ) - - lazy val ServiceMutationResponseType = makeMutationResponseType[Service]( - "MutateService", - "desc here", - ServiceType - ) - - lazy val ServiceColumnMutationResponseType = makeMutationResponseType[ServiceColumn]( - "MutateServiceColumn", - "desc here", - ServiceColumnType - ) - - lazy val LabelMutationResponseType = makeMutationResponseType[Label]( - "MutateLabel", - "desc here", - LabelType - ) - - lazy val labelField: Field[GraphRepository, Any] = Field( - "Label", - OptionType(LabelType), - description = Option("desc here"), - arguments = Argument("name", EnumLabelsType, description = "desc here") :: Nil, - resolve = { c => - val labelName = c.arg[String]("name") - c.ctx.allLabels().find(_.label == labelName) - } - ) - - lazy val labelsField: Field[GraphRepository, Any] = Field( - "Labels", - ListType(LabelType), - description = Option("desc here"), - arguments = List(LabelNameArg), - resolve = { c => - c.argOpt[String]("name") match { - case Some(name) => c.ctx.allLabels().filter(_.label == name) - case None => c.ctx.allLabels() - } - } - ) - - val serviceOptArgs = List( - "compressionAlgorithm" -> CompressionAlgorithmType, - "cluster" -> StringType, - "hTableName" -> StringType, - "preSplitSize" -> IntType, - "hTableTTL" -> IntType - ).map { case (name, _type) => Argument(name, OptionInputType(_type)) } - - val AddPropServiceType = InputObjectType[ServiceColumnParam]( - "Input_Service_ServiceColumn_Props", - description = "desc", - fields = DummyInputField +: serviceColumnOnServiceWithPropInputObjectFields - ) - - val ServiceColumnSelectType = InputObjectType[ServiceColumnParam]( - "Input_Service_ServiceColumn", - description = "desc", - fields = DummyInputField +: serviceColumnOnServiceInputObjectFields - ) - - val InputServiceType = InputObjectType[ServiceColumnParam]( - "Input_Service", - description = "desc", - fields = DummyInputField +: serviceColumnOnServiceInputObjectFields - ) - - lazy val serviceField: Field[GraphRepository, Any] = Field( - "Service", - OptionType(ServiceType), - description = Option("desc here"), - arguments = Argument("name", ServiceListType, description = "desc here") :: Nil, - resolve = { c => - val serviceName = c.arg[String]("name") - c.ctx.allServices.find(_.serviceName == serviceName) - } - ) - - lazy val servicesField: Field[GraphRepository, Any] = Field( - "Services", - ListType(ServiceType), - description = Option("desc here"), - arguments = List(ServiceNameArg), - resolve = { c => - c.argOpt[String]("name") match { - case Some(name) => c.ctx.allServices.filter(_.serviceName == name) - case None => c.ctx.allServices - } - } - ) - /** - * Query Fields - * Provide s2graph management query API - */ - lazy val queryFields: List[Field[GraphRepository, Any]] = List(serviceField, servicesField, labelField, labelsField) - - /** - * Mutation fields - * Provide s2graph management mutate API - * - * - createService - * - createLabel - * - ... - */ - - lazy val labelRequiredArg = List( - Argument("sourceService", InputServiceType), - Argument("targetService", InputServiceType) - ) - - val labelOptsArgs = List( - Argument("serviceName", OptionInputType(ServiceListType)), - Argument("consistencyLevel", OptionInputType(ConsistencyLevelType)), - Argument("isDirected", OptionInputType(BooleanType)), - Argument("isAsync", OptionInputType(BooleanType)), - Argument("schemaVersion", OptionInputType(StringType)) - ) - - val NameArg = Argument("name", StringType, description = "desc here") - - lazy val ServiceNameArg = Argument("name", OptionInputType(ServiceListType), description = "desc here") - - lazy val ServiceNameRawArg = Argument("serviceName", ServiceListType, description = "desc here") - - lazy val ColumnNameArg = Argument("columnName", OptionInputType(ServiceColumnListType), description = "desc here") - - lazy val ColumnTypeArg = Argument("columnType", DataTypeType, description = "desc here") - - lazy val LabelNameArg = Argument("name", OptionInputType(EnumLabelsType), description = "desc here") - - lazy val PropArg = Argument("props", OptionInputType(ListInputType(InputPropType)), description = "desc here") - - lazy val IndicesArg = Argument("indices", OptionInputType(ListInputType(InputIndexType)), description = "desc here") - - lazy val mutationFields: List[Field[GraphRepository, Any]] = List( - Field("createService", - ServiceMutationResponseType, - arguments = NameArg :: serviceOptArgs, - resolve = c => MutationResponse(c.ctx.createService(c.args)) - ), - Field("createLabel", - LabelMutationResponseType, - arguments = NameArg :: PropArg :: IndicesArg :: labelRequiredArg ::: labelOptsArgs, - resolve = c => MutationResponse(c.ctx.createLabel(c.args)) - ), - Field("deleteLabel", - LabelMutationResponseType, - arguments = LabelNameArg :: Nil, - resolve = c => MutationResponse(c.ctx.deleteLabel(c.args)) - ), - Field("createServiceColumn", - ServiceColumnMutationResponseType, - arguments = List(ServiceNameRawArg, Argument("columnName", StringType), ColumnTypeArg, PropArg), - resolve = c => MutationResponse(c.ctx.createServiceColumn(c.args)) - ), - Field("deleteServiceColumn", - ServiceColumnMutationResponseType, - arguments = Argument("service", ServiceColumnSelectType) :: Nil, - resolve = c => MutationResponse(c.ctx.deleteServiceColumn(c.args)) - ), - Field("addPropsToServiceColumn", - ServiceColumnMutationResponseType, - arguments = Argument("service", AddPropServiceType) :: Nil, - resolve = c => MutationResponse(c.ctx.addPropsToServiceColumn(c.args)) - ), - Field("addPropsToLabel", - LabelMutationResponseType, - arguments = Argument("labelName", EnumLabelsType) :: PropArg :: Nil, - resolve = c => MutationResponse(c.ctx.addPropsToLabel(c.args)) - ) - ) -} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala index dc00ce2..eff0350 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/S2Type.scala @@ -19,14 +19,16 @@ package org.apache.s2graph.graphql.types +import scala.concurrent._ import org.apache.s2graph.core.Management.JsonModel.{Index, Prop} import org.apache.s2graph.core._ import org.apache.s2graph.core.mysqls._ import org.apache.s2graph.graphql.repository.GraphRepository import sangria.schema._ +import org.apache.s2graph.graphql.bind.{AstHelper, Unmarshaller} +import org.apache.s2graph.graphql.types.StaticTypes._ import scala.language.existentials -import org.apache.s2graph.graphql.marshaller._ object S2Type { @@ -81,9 +83,9 @@ object S2Type { def makeInputFieldsOnService(service: Service): Seq[InputField[Any]] = { val inputFields = service.serviceColumns(false).map { serviceColumn => - val idField = InputField("id", s2TypeToScalarType(serviceColumn.columnType)) + val idField = InputField("id", toScalarType(serviceColumn.columnType)) val propFields = serviceColumn.metasWithoutCache.filter(ColumnMeta.isValid).map { lm => - InputField(lm.name, OptionInputType(s2TypeToScalarType(lm.dataType))) + InputField(lm.name, OptionInputType(toScalarType(lm.dataType))) } val vertexMutateType = InputObjectType[Map[String, Any]]( @@ -100,68 +102,62 @@ object S2Type { def makeInputFieldsOnLabel(label: Label): Seq[InputField[Any]] = { val propFields = label.labelMetaSet.toList.map { lm => - InputField(lm.name, OptionInputType(s2TypeToScalarType(lm.dataType))) + InputField(lm.name, OptionInputType(toScalarType(lm.dataType))) } val labelFields = List( InputField("timestamp", OptionInputType(LongType)), - InputField("from", s2TypeToScalarType(label.srcColumnType)), - InputField("to", s2TypeToScalarType(label.srcColumnType)), - InputField("direction", OptionInputType(DirectionType)) + InputField("from", toScalarType(label.srcColumnType)), + InputField("to", toScalarType(label.srcColumnType)), + InputField("direction", OptionInputType(BothDirectionType)) ) labelFields.asInstanceOf[Seq[InputField[Any]]] ++ propFields.asInstanceOf[Seq[InputField[Any]]] } - def makeServiceColumnFields(column: ServiceColumn): List[Field[GraphRepository, Any]] = { + def makeServiceColumnFields(column: ServiceColumn, allLabels: Seq[Label]): List[Field[GraphRepository, Any]] = { val reservedFields = List("id" -> column.columnType, "timestamp" -> "long") val columnMetasKv = column.metasWithoutCache.filter(ColumnMeta.isValid).map { columnMeta => columnMeta.name -> columnMeta.dataType } - (reservedFields ++ columnMetasKv).map { case (k, v) => makePropField(k, v) } - } + val (sameLabel, diffLabel) = allLabels.toList.partition(l => l.srcColumn == l.tgtColumn) - def makeServiceField(service: Service, allLabels: List[Label])(implicit repo: GraphRepository): List[Field[GraphRepository, Any]] = { - lazy val columnsOnService = service.serviceColumns(false).toList.map { column => + val outLabels = diffLabel.filter(l => column == l.srcColumn).distinct.toList + val inLabels = diffLabel.filter(l => column == l.tgtColumn).distinct.toList + val inOutLabels = sameLabel.filter(l => l.srcColumn == column && l.tgtColumn == column) - val outLabels = allLabels.filter { lb => column.id.get == lb.srcColumn.id.get }.distinct - val inLabels = allLabels.filter { lb => column.id.get == lb.tgtColumn.id.get }.distinct + lazy val columnFields = (reservedFields ++ columnMetasKv).map { case (k, v) => makePropField(k, v) } - lazy val vertexPropFields = makeServiceColumnFields(column) + lazy val outLabelFields: List[Field[GraphRepository, Any]] = outLabels.map(l => makeLabelField("out", l, allLabels)) + lazy val inLabelFields: List[Field[GraphRepository, Any]] = inLabels.map(l => makeLabelField("in", l, allLabels)) + lazy val inOutLabelFields: List[Field[GraphRepository, Any]] = inOutLabels.map(l => makeLabelField("both", l, allLabels)) + lazy val propsType = wrapField(s"ServiceColumn_${column.service.serviceName}_${column.columnName}_props", "props", columnFields) - lazy val outLabelFields: List[Field[GraphRepository, Any]] = - outLabels.map(l => makeLabelField("out", l, allLabels)) + lazy val labelFieldNameSet = (outLabels ++ inLabels ++ inOutLabels).map(_.label).toSet - lazy val inLabelFields: List[Field[GraphRepository, Any]] = - inLabels.map(l => makeLabelField("in", l, allLabels)) + propsType :: inLabelFields ++ outLabelFields ++ inOutLabelFields ++ columnFields.filterNot(cf => labelFieldNameSet(cf.name)) + } - lazy val connectedLabelType = ObjectType( - s"Input_${service.serviceName}_${column.columnName}", - () => fields[GraphRepository, Any](vertexPropFields ++ (inLabelFields ++ outLabelFields): _*) + def makeServiceField(service: Service, allLabels: List[Label])(implicit repo: GraphRepository): List[Field[GraphRepository, Any]] = { + lazy val columnsOnService = service.serviceColumns(false).toList.map { column => + lazy val serviceColumnFields = makeServiceColumnFields(column, allLabels) + lazy val ColumnType = ObjectType( + s"ServiceColumn_${service.serviceName}_${column.columnName}", + () => fields[GraphRepository, Any](serviceColumnFields: _*) ) val v = Field(column.columnName, - ListType(connectedLabelType), + ListType(ColumnType), arguments = List( - Argument("id", OptionInputType(s2TypeToScalarType(column.columnType))), - Argument("ids", OptionInputType(ListInputType(s2TypeToScalarType(column.columnType)))), - Argument("search", OptionInputType(StringType)) + Argument("id", OptionInputType(toScalarType(column.columnType))), + Argument("ids", OptionInputType(ListInputType(toScalarType(column.columnType)))) ), description = Option("desc here"), resolve = c => { implicit val ec = c.ctx.ec + val (vertices, canSkipFetchVertex) = Unmarshaller.serviceColumnFieldOnService(column, c) - val ids = c.argOpt[Any]("id").toSeq ++ c.argOpt[List[Any]]("ids").toList.flatten - val vertices = ids.map(vid => c.ctx.toS2VertexLike(vid, column)) - - val columnFields = column.metasInvMap.keySet - val selectedFields = c.astFields.flatMap { f => - f.selections.map(s => s.asInstanceOf[sangria.ast.Field].name) - } - - val passFetchVertex = selectedFields.forall(f => f == "id" || !columnFields(f)) - - if (passFetchVertex) scala.concurrent.Future.successful(vertices) - else repo.getVertices(vertices) // fill props + if (canSkipFetchVertex) Future.successful(vertices) + else c.ctx.getVertices(vertices) } ): Field[GraphRepository, Any] @@ -171,72 +167,48 @@ object S2Type { columnsOnService } - def fillPartialVertex(vertex: S2VertexLike, - column: ServiceColumn, - c: Context[GraphRepository, Any]): scala.concurrent.Future[S2VertexLike] = { - implicit val ec = c.ctx.ec - - val columnFields = column.metasInvMap.keySet - val selectedFields = c.astFields.flatMap { f => - f.selections.map(s => s.asInstanceOf[sangria.ast.Field].name) - } - - // Vertex on edge has invalid `serviceColumn` info - lazy val newVertex = c.ctx.toS2VertexLike(vertex.innerId, column) - - val passFetchVertex = selectedFields.forall(f => f == "id" || !columnFields(f)) - - if (passFetchVertex) scala.concurrent.Future.successful(vertex) - else c.ctx.getVertices(Seq(newVertex)).map(_.head) // fill props - } - - def makeLabelField(dir: String, label: Label, allLabels: List[Label]): Field[GraphRepository, Any] = { + def makeLabelField(dir: String, label: Label, allLabels: Seq[Label]): Field[GraphRepository, Any] = { val labelReserved = List("direction" -> "string", "timestamp" -> "long") val labelProps = label.labelMetas.map { lm => lm.name -> lm.dataType } - lazy val edgeFields: List[Field[GraphRepository, Any]] = - (labelReserved ++ labelProps).map { case (k, v) => makePropField(k, v) } - val column = if (dir == "out") label.tgtColumn else label.srcColumn - lazy val toType = ObjectType(s"Label_${label.label}_${column.columnName}", () => { - lazy val linked = if (dir == "out") { - val inLabels = allLabels.filter { lb => column.id.get == lb.tgtColumn.id.get }.distinct - inLabels.map(l => makeLabelField("in", l, allLabels)) - } else { - val outLabels = allLabels.filter { lb => column.id.get == lb.srcColumn.id.get }.distinct - outLabels.map(l => makeLabelField("out", l, allLabels)) - } + lazy val labelFields: List[Field[GraphRepository, Any]] = + (labelReserved ++ labelProps).map { case (k, v) => makePropField(k, v) } - makeServiceColumnFields(column) ++ linked - }) + lazy val labelPropField = wrapField(s"Label_${label.label}_props", "props", labelFields) - lazy val toField: Field[GraphRepository, Any] = Field(column.columnName, toType, resolve = c => { - val vertex = if (dir == "out") { - c.value.asInstanceOf[S2EdgeLike].tgtVertex - } else { - c.value.asInstanceOf[S2EdgeLike].srcVertex - } + lazy val labelColumnType = ObjectType(s"Label_${label.label}_${column.columnName}", + () => makeServiceColumnFields(column, allLabels) + ) - fillPartialVertex(vertex, column, c) + lazy val serviceColumnField: Field[GraphRepository, Any] = Field(column.columnName, labelColumnType, resolve = c => { + implicit val ec = c.ctx.ec + val (vertex, canSkipFetchVertex) = Unmarshaller.serviceColumnFieldOnLabel(column, c) + + if (canSkipFetchVertex) Future.successful(vertex) + else c.ctx.getVertices(Seq(vertex)).map(_.head) // fill props }) lazy val EdgeType = ObjectType( s"Label_${label.label}_${column.columnName}_${dir}", - () => fields[GraphRepository, Any](List(toField) ++ edgeFields: _*) + () => fields[GraphRepository, Any]( + List(serviceColumnField, labelPropField) ++ labelFields.filterNot(_.name == column.columnName): _*) ) + val dirArgs = dir match { + case "in" => Argument("direction", OptionInputType(InDirectionType), "desc here", defaultValue = "in") :: Nil + case "out" => Argument("direction", OptionInputType(OutDirectionType), "desc here", defaultValue = "out") :: Nil + case "both" => Argument("direction", OptionInputType(BothDirectionType), "desc here", defaultValue = "out") :: Nil + } + lazy val edgeTypeField: Field[GraphRepository, Any] = Field( s"${label.label}", ListType(EdgeType), - arguments = Argument("direction", OptionInputType(DirectionType), "desc here", defaultValue = "out") :: Nil, + arguments = dirArgs, description = Some("fetch edges"), resolve = { c => - val vertex: S2VertexLike = c.value match { - case v: S2VertexLike => v - case _ => throw new IllegalArgumentException(s"ERROR: ${c.value.getClass}") - } - + val (vertex, dir) = Unmarshaller.labelField(c) c.ctx.getEdges(vertex, label, dir) } ) @@ -249,6 +221,7 @@ object S2Type { class S2Type(repo: GraphRepository) { import S2Type._ + import org.apache.s2graph.graphql.bind.Unmarshaller._ implicit val graphRepository = repo http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SangriaPlayJsonScalarType.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SangriaPlayJsonScalarType.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SangriaPlayJsonScalarType.scala deleted file mode 100644 index 723392c..0000000 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SangriaPlayJsonScalarType.scala +++ /dev/null @@ -1,76 +0,0 @@ -///* -// * 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.s2graph.graphql.types -// -//import sangria.ast._ -//import sangria.validation.ValueCoercionViolation -// -//// https://gist.github.com/OlegIlyenko/5b96f4b54f656aac226d3c4bc33fd2a6 -// -//object PlayJsonPolyType { -// -// import play.api.libs.json._ -// import sangria.ast -// import sangria.schema._ -// -// case object JsonCoercionViolation extends ValueCoercionViolation("Not valid JSON") -// -// def scalarTypeToJsValue(v: sangria.ast.Value): JsValue = v match { -// case v: IntValue => JsNumber(v.value) -// case v: BigIntValue => JsNumber(BigDecimal(v.value.bigInteger)) -// case v: FloatValue => JsNumber(v.value) -// case v: BigDecimalValue => JsNumber(v.value) -// case v: StringValue => JsString(v.value) -// case v: BooleanValue => JsBoolean(v.value) -// case v: ListValue => JsNull -// case v: VariableValue => JsNull -// case v: NullValue => JsNull -// case v: ObjectValue => JsNull -// case _ => throw new RuntimeException("Error!") -// } -// -// implicit val PolyType = ScalarType[JsValue]("Poly", -// description = Some("Type Poly = String | Number | Boolean"), -// coerceOutput = (value, _) â value match { -// case JsString(s) => s -// case JsNumber(n) => n -// case JsBoolean(b) => b -// case JsNull => null -// case _ => 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(v.toDouble)) -// case v: Double => Right(JsNumber(v)) -// case v: BigInt => Right(JsNumber(BigDecimal(v))) -// case v: BigDecimal => Right(JsNumber(v)) -// case _ => Left(JsonCoercionViolation) -// }, -// coerceInput = { -// case value: ast.StringValue => Right(JsString(value.value)) -// case value: ast.IntValue => Right(JsNumber(value.value)) -// case value: ast.FloatValue => Right(JsNumber(value.value)) -// case value: ast.BigIntValue => Right(JsNumber(BigDecimal(value.value.bigInteger))) -// case _ => Left(JsonCoercionViolation) -// }) -//} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala index 6fa1bd1..27b0c50 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/SchemaDef.scala @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.s2graph.graphql +package org.apache.s2graph.graphql.types import org.apache.s2graph.graphql.repository.GraphRepository import org.apache.s2graph.graphql.types._ @@ -33,7 +33,7 @@ class SchemaDef(g: GraphRepository) { import sangria.schema._ val s2Type = new S2Type(g) - val s2ManagementType = new S2ManagementType(g) + val s2ManagementType = new ManagementType(g) val queryManagementFields = List(wrapField("QueryManagement", "Management", s2ManagementType.queryFields)) val S2QueryType = ObjectType[GraphRepository, Any]( http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/StaticType.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/StaticType.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/StaticType.scala new file mode 100644 index 0000000..aca832c --- /dev/null +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/StaticType.scala @@ -0,0 +1,149 @@ +/* + * 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.s2graph.graphql.types + +import org.apache.s2graph.core.Management.JsonModel._ +import org.apache.s2graph.core.{JSONParser, S2EdgeLike, S2VertexLike} +import org.apache.s2graph.core.mysqls._ +import org.apache.s2graph.core.storage.MutateResponse +import org.apache.s2graph.graphql.repository.GraphRepository +import sangria.macros.derive._ +import sangria.schema._ + +import scala.util.{Failure, Random, Success, Try} + +object StaticTypes { + val MutateResponseType = deriveObjectType[GraphRepository, MutateResponse]( + ObjectTypeName("MutateGraphElement"), + ObjectTypeDescription("desc here"), + AddFields( + Field("isSuccess", BooleanType, resolve = c => c.value.isSuccess) + ) + ) + + val DataTypeType = EnumType( + "Enum_DataType", + description = Option("desc here"), + values = List( + EnumValue("string", value = "string"), + EnumValue("int", value = "int"), + EnumValue("long", value = "long"), + EnumValue("double", value = "double"), + EnumValue("boolean", value = "boolean") + ) + ) + + val InDirectionType = EnumType( + "Enum_Direction_In", + description = Option("desc here"), + values = List( + EnumValue("in", value = "in") + ) + ) + + val OutDirectionType = EnumType( + "Enum_Direction_Out", + description = Option("desc here"), + values = List( + EnumValue("out", value = "out") + ) + ) + val BothDirectionType = EnumType( + "Enum_Direction_Both", + description = Option("desc here"), + values = List( + EnumValue("out", value = "out"), + EnumValue("in", value = "in") + ) + ) + + val LabelMetaType = deriveObjectType[GraphRepository, LabelMeta]( + ObjectTypeName("LabelMeta"), + ExcludeFields("seq", "labelId") + ) + + val ColumnMetaType = deriveObjectType[GraphRepository, ColumnMeta]( + ObjectTypeName("ColumnMeta"), + ExcludeFields("seq", "columnId") + ) + + val InputIndexType = InputObjectType[Index]( + "Input_Index", + description = "desc here", + fields = List( + InputField("name", StringType), + InputField("propNames", ListInputType(StringType)) + ) + ) + + val InputPropType = InputObjectType[Prop]( + "Input_Prop", + description = "desc here", + fields = List( + InputField("name", StringType), + InputField("dataType", DataTypeType), + InputField("defaultValue", StringType), + InputField("storeInGlobalIndex", BooleanType) + ) + ) + + val CompressionAlgorithmType = EnumType( + "Enum_CompressionAlgorithm", + description = Option("desc here"), + values = List( + EnumValue("gz", description = Option("desc here"), value = "gz"), + EnumValue("lz4", description = Option("desc here"), value = "lz4") + ) + ) + + val ConsistencyLevelType = EnumType( + "Enum_Consistency", + description = Option("desc here"), + values = List( + EnumValue("weak", description = Option("desc here"), value = "weak"), + EnumValue("strong", description = Option("desc here"), value = "strong") + ) + ) + + val LabelIndexType = deriveObjectType[GraphRepository, LabelIndex]( + ObjectTypeName("LabelIndex"), + ObjectTypeDescription("desc here"), + ExcludeFields("seq", "metaSeqs", "formulars", "labelId") + ) + + val LabelType = deriveObjectType[GraphRepository, Label]( + ObjectTypeName("Label"), + ObjectTypeDescription("desc here"), + AddFields( + Field("indices", ListType(LabelIndexType), resolve = c => c.value.indices), + Field("props", ListType(LabelMetaType), resolve = c => c.value.labelMetas) + ), + RenameField("label", "name") + ) + + val DummyInputField = InputField("_", OptionInputType(LongType)) + + val DummyObjectTypeField: Field[GraphRepository, Any] = Field( + "_", + OptionType(LongType), + description = Some("dummy field"), + resolve = _ => None + ) +} http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala index 754f377..2b648e2 100644 --- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala +++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/package.scala @@ -1,43 +1,16 @@ -/* - * 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.s2graph.graphql -import org.apache.s2graph.core.Management.JsonModel._ -import org.apache.s2graph.core.{JSONParser, S2EdgeLike, S2VertexLike} -import org.apache.s2graph.core.mysqls._ -import org.apache.s2graph.core.storage.MutateResponse import org.apache.s2graph.graphql.repository.GraphRepository -import sangria.macros.derive._ import sangria.schema._ -import scala.util.{Failure, Random, Success, Try} - package object types { - def wrapField(objectName: String, fieldName: String, fields: List[Field[GraphRepository, Any]]): Field[GraphRepository, Any] = { - val ManagementType = ObjectType(objectName, fields = fields) - val f: Field[GraphRepository, Any] = Field(fieldName, ManagementType, resolve = c => c.value) - f + def wrapField(objectName: String, fieldName: String, fields: Seq[Field[GraphRepository, Any]]): Field[GraphRepository, Any] = { + val tpe = ObjectType(objectName, fields = fields.toList) + Field(fieldName, tpe, resolve = c => c.value): Field[GraphRepository, Any] } - def s2TypeToScalarType(from: String): ScalarType[_] = from match { + def toScalarType(from: String): ScalarType[_] = from match { case "string" => StringType case "int" => IntType case "integer" => IntType @@ -47,106 +20,4 @@ package object types { case "boolean" => BooleanType case "bool" => BooleanType } - - val MutateResponseType = deriveObjectType[GraphRepository, MutateResponse]( - ObjectTypeName("MutateGraphElement"), - ObjectTypeDescription("desc here"), - AddFields( - Field("isSuccess", BooleanType, resolve = c => c.value.isSuccess) - ) - ) - - val DataTypeType = EnumType( - "Enum_DataType", - description = Option("desc here"), - values = List( - EnumValue("string", value = "string"), - EnumValue("int", value = "int"), - EnumValue("long", value = "long"), - EnumValue("double", value = "double"), - EnumValue("boolean", value = "boolean") - ) - ) - - val DirectionType = EnumType( - "Enum_Direction", - description = Option("desc here"), - values = List( - EnumValue("out", value = "out"), - EnumValue("in", value = "in") - ) - ) - - val LabelMetaType = deriveObjectType[GraphRepository, LabelMeta]( - ObjectTypeName("LabelMeta"), - ExcludeFields("seq", "labelId") - ) - - val ColumnMetaType = deriveObjectType[GraphRepository, ColumnMeta]( - ObjectTypeName("ColumnMeta"), - ExcludeFields("seq", "columnId") - ) - - val InputIndexType = InputObjectType[Index]( - "Input_Index", - description = "desc here", - fields = List( - InputField("name", StringType), - InputField("propNames", ListInputType(StringType)) - ) - ) - - val InputPropType = InputObjectType[Prop]( - "Input_Prop", - description = "desc here", - fields = List( - InputField("name", StringType), - InputField("dataType", DataTypeType), - InputField("defaultValue", StringType), - InputField("storeInGlobalIndex", BooleanType) - ) - ) - - val CompressionAlgorithmType = EnumType( - "Enum_CompressionAlgorithm", - description = Option("desc here"), - values = List( - EnumValue("gz", description = Option("desc here"), value = "gz"), - EnumValue("lz4", description = Option("desc here"), value = "lz4") - ) - ) - - val ConsistencyLevelType = EnumType( - "Enum_Consistency", - description = Option("desc here"), - values = List( - EnumValue("weak", description = Option("desc here"), value = "weak"), - EnumValue("strong", description = Option("desc here"), value = "strong") - ) - ) - - val LabelIndexType = deriveObjectType[GraphRepository, LabelIndex]( - ObjectTypeName("LabelIndex"), - ObjectTypeDescription("desc here"), - ExcludeFields("seq", "metaSeqs", "formulars", "labelId") - ) - - val LabelType = deriveObjectType[GraphRepository, Label]( - ObjectTypeName("Label"), - ObjectTypeDescription("desc here"), - AddFields( - Field("indices", ListType(LabelIndexType), resolve = c => c.value.indices), - Field("props", ListType(LabelMetaType), resolve = c => c.value.labelMetas) - ), - RenameField("label", "name") - ) - - val DummyInputField = InputField("_", OptionInputType(LongType)) - - val DummyObjectTypeField: Field[GraphRepository, Any] = Field( - "_", - OptionType(LongType), - description = Some("dummy field"), - resolve = _ => None - ) } http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala b/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala index 8cbff19..81bf884 100644 --- a/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala +++ b/s2graphql/src/test/scala/org/apache/s2graph/graphql/ScenarioTest.scala @@ -179,7 +179,7 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { query { Management { - Service(name: kakao) { + Services(name: kakao) { name serviceColumns { name @@ -200,7 +200,7 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { { "data": { "Management": { - "Service": { + "Services": [{ "name": "kakao", "serviceColumns": [{ "name": "user", @@ -209,7 +209,7 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { { "name": "gender", "dataType": "string" } ] }] - } + }] } } } @@ -307,7 +307,7 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { query { Management { - Label(name: friends) { + Labels(name: friends) { name props { name @@ -325,13 +325,13 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { { "data": { "Management": { - "Label": { + "Labels": [{ "name": "friends", "props": [{ "name": "score", "dataType": "double" }] - } + }] } } } @@ -392,6 +392,9 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { id age gender + props { + age + } } } } @@ -406,11 +409,17 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { "user": [{ "id": "daewon", "age": 20, - "gender": "M" + "gender": "M", + "props": { + "age": 20 + } }, { "id": "shon", "age": 19, - "gender": "F" + "gender": "F", + "props": { + "age": 19 + } }] } } @@ -464,13 +473,16 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { kakao { user(id: "daewon") { id - friends { - score - to { + friends(direction: out) { + props { + score + } + score # shorcut score props + user { id age friends(direction: in) { - to { + user { id age } @@ -492,12 +504,15 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { "user" : [ { "id" : "daewon", "friends" : [ { + "props" : { + "score" : 0.9 + }, "score" : 0.9, - "to" : { + "user" : { "id" : "shon", "age" : 19, "friends" : [ { - "to" : { + "user" : { "id" : "daewon", "age" : 20 }, @@ -564,14 +579,13 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { query { Management { - Label(name: friends) { + Labels(name: friends) { name props { name dataType } } - } } """ @@ -586,7 +600,7 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { query { Management { - Service(name: kakao) { + Services(name: kakao) { serviceColumns { name } @@ -602,9 +616,9 @@ class ScenarioTest extends FunSpec with Matchers with BeforeAndAfterAll { { "data": { "Management": { - "Service": { + "Services": [{ "serviceColumns": [] - } + }] } } } http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/test/scala/org/apache/s2graph/graphql/SchemaTest.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/test/scala/org/apache/s2graph/graphql/SchemaTest.scala b/s2graphql/src/test/scala/org/apache/s2graph/graphql/SchemaTest.scala index fdedfe9..8b2b3fe 100644 --- a/s2graphql/src/test/scala/org/apache/s2graph/graphql/SchemaTest.scala +++ b/s2graphql/src/test/scala/org/apache/s2graph/graphql/SchemaTest.scala @@ -69,7 +69,7 @@ class SchemaTest extends FunSuite with Matchers with BeforeAndAfterAll { Map("name" -> "Enum_CompressionAlgorithm"), Map("name" -> "Enum_Consistency"), Map("name" -> "Enum_DataType"), - Map("name" -> "Enum_Direction"), + Map("name" -> "Enum_Direction_Both"), Map("name" -> "Enum_Service"), Map("name" -> "Enum_Label"), Map("name" -> "Enum_kakao_ServiceColumn"), @@ -102,14 +102,16 @@ class SchemaTest extends FunSuite with Matchers with BeforeAndAfterAll { Map("name" -> "Input_kakao_ServiceColumn_Props"), Map("name" -> "Input_kakao_ServiceColumn"), - Map("name" -> "Input_kakao_user"), Map("name" -> "Input_kakao_user_vertex_mutate"), Map("name" -> "Service_kakao"), - Map("name" -> "Label_friends"), - Map("name" -> "Label_friends_from"), - Map("name" -> "Label_friends_to"), + Map("name" -> "Label_friends_user"), + Map("name" -> "Label_friends_user_both"), + + Map("name" -> "ServiceColumn_kakao_user_props"), + Map("name" -> "ServiceColumn_kakao_user"), + Map("name" -> "Label_friends_props"), // root object type Map("name" -> "Query"), http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/da15232c/s2graphql/src/test/scala/org/apache/s2graph/graphql/TestGraph.scala ---------------------------------------------------------------------- diff --git a/s2graphql/src/test/scala/org/apache/s2graph/graphql/TestGraph.scala b/s2graphql/src/test/scala/org/apache/s2graph/graphql/TestGraph.scala index 8b3c0ae..46db9fc 100644 --- a/s2graphql/src/test/scala/org/apache/s2graph/graphql/TestGraph.scala +++ b/s2graphql/src/test/scala/org/apache/s2graph/graphql/TestGraph.scala @@ -25,6 +25,7 @@ import org.apache.s2graph.core.mysqls.{Label, Model, Service} import org.apache.s2graph.core.rest.RequestParser import org.apache.s2graph.core.{Management, S2Graph} import org.apache.s2graph.graphql.repository.GraphRepository +import org.apache.s2graph.graphql.types.SchemaDef import play.api.libs.json._ import sangria.ast.Document import sangria.execution.Executor
