remove inefficiency logic
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 5f1c225..a4a61ff 100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/GraphQLServer.scala
@@ -45,7 +45,7 @@
import scala.util.control.NonFatal
import scala.util.{Failure, Success, Try}
-object GraphQLServer {
+class GraphQLServer() {
val className = Schema.getClass.getName
val logger = LoggerFactory.getLogger(this.getClass)
@@ -58,6 +58,7 @@
val config = ConfigFactory.load()
val s2graph = new S2Graph(config)
val schemaCacheTTL = Try(config.getInt("schemaCacheTTL")).getOrElse(-1)
+ val withAdmin = Try(config.getBoolean("schemaCacheTTL")).getOrElse(false)
val s2Repository = new GraphRepository(s2graph)
val schemaConfig = ConfigFactory.parseMap(Map(
SafeUpdateCache.MaxSizeKey -> 1, SafeUpdateCache.TtlKey -> schemaCacheTTL
@@ -77,15 +78,24 @@
ret
}
+ logger.info(s"schemaCacheTTL: ${schemaCacheTTL}")
+
+ val schemaCacheKey = className + "s2Schema"
+
+ schemaCache.put(schemaCacheKey, createNewSchema(withAdmin))
+
/**
* In development mode(schemaCacheTTL = -1),
* a new schema is created for each request.
*/
- logger.info(s"schemaCacheTTL: ${schemaCacheTTL}")
- private def createNewSchema(): Schema[GraphRepository, Any] = {
- val newSchema = new SchemaDef(s2Repository).S2GraphSchema
- logger.info(s"Schema updated: ${System.currentTimeMillis()}")
+ private def createNewSchema(withAdmin: Boolean): Schema[GraphRepository, Any] = {
+ logger.info(s"Schema update start")
+
+ val ts = System.currentTimeMillis()
+ val newSchema = new SchemaDef(s2Repository, withAdmin).S2GraphSchema
+
+ logger.info(s"Schema updated: ${(System.currentTimeMillis() - ts) / 1000} sec")
newSchema
}
@@ -115,8 +125,7 @@
def executeGraphQLQuery(query: Document, op: Option[String], vars: JsObject)(implicit e: ExecutionContext) = {
import GraphRepository._
- val cacheKey = className + "s2Schema"
- val s2schema = schemaCache.withCache(cacheKey, broadcast = false, onEvict = onEvictSchema)(createNewSchema())
+ val s2schema = schemaCache.withCache(schemaCacheKey, broadcast = false, onEvict = onEvictSchema)(createNewSchema(withAdmin))
val resolver: DeferredResolver[GraphRepository] = DeferredResolver.fetchers(vertexFetcher, edgeFetcher)
val includeGrpaph = vars.fields.get("includeGraph").contains(spray.json.JsBoolean(true))
diff --git a/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala b/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala
index 65ff348..8b89c73 100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/HttpServer.scala
@@ -54,12 +54,14 @@
import spray.json.DefaultJsonProtocol._
+ val graphQLServer = new GraphQLServer()
+
val route: Route =
get {
getFromResource("assets/graphiql.html")
} ~ (post & path("updateEdgeFetcher")) {
entity(as[JsValue]) { body =>
- GraphQLServer.updateEdgeFetcher(body) match {
+ graphQLServer.updateEdgeFetcher(body) match {
case Success(_) => complete(StatusCodes.OK -> JsString("Update fetcher finished"))
case Failure(e) =>
logger.error("Error on execute", e)
@@ -70,11 +72,11 @@
parameters('operationName.?, 'variables.?) { (operationNameParam, variablesParam) =>
entity(as[Document]) { document ⇒
variablesParam.map(parseJson) match {
- case None ⇒ complete(GraphQLServer.executeGraphQLQuery(document, operationNameParam, JsObject()))
- case Some(Right(js)) ⇒ complete(GraphQLServer.executeGraphQLQuery(document, operationNameParam, js.asJsObject))
+ case None ⇒ complete(graphQLServer.executeGraphQLQuery(document, operationNameParam, JsObject()))
+ case Some(Right(js)) ⇒ complete(graphQLServer.executeGraphQLQuery(document, operationNameParam, js.asJsObject))
case Some(Left(e)) ⇒
logger.error("Error on execute", e)
- complete(StatusCodes.BadRequest -> GraphQLServer.formatError(e))
+ complete(StatusCodes.BadRequest -> graphQLServer.formatError(e))
}
} ~ entity(as[JsValue]) { body ⇒
val fields = body.asJsObject.fields
@@ -84,13 +86,13 @@
val variables = fields.get("variables").filterNot(_ == JsNull)
query.map(QueryParser.parse(_)) match {
- case None ⇒ complete(StatusCodes.BadRequest -> GraphQLServer.formatError("No query to execute"))
+ case None ⇒ complete(StatusCodes.BadRequest -> graphQLServer.formatError("No query to execute"))
case Some(Failure(error)) ⇒
logger.error("Error on execute", error)
- complete(StatusCodes.BadRequest -> GraphQLServer.formatError(error))
+ complete(StatusCodes.BadRequest -> graphQLServer.formatError(error))
case Some(Success(document)) => variables match {
- case Some(js) ⇒ complete(GraphQLServer.executeGraphQLQuery(document, operationName, js.asJsObject))
- case None ⇒ complete(GraphQLServer.executeGraphQLQuery(document, operationName, JsObject()))
+ case Some(js) ⇒ complete(graphQLServer.executeGraphQLQuery(document, operationName, js.asJsObject))
+ case None ⇒ complete(graphQLServer.executeGraphQLQuery(document, operationName, JsObject()))
}
}
}
@@ -101,7 +103,9 @@
logger.info(s"Starting GraphQL server... $port")
- Http().bindAndHandle(route, "0.0.0.0", port)
+ Http().bindAndHandle(route, "0.0.0.0", port).foreach { binding =>
+ logger.info(s"GraphQL server ready for connect")
+ }
def shutdown(): Unit = {
logger.info("Terminating...")
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 b5e65dc..bd98504 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
@@ -273,14 +273,29 @@
def deleteLabel(args: Args): Try[Label] = {
val labelName = args.arg[String]("name")
-
val deleteLabelTry = Management.deleteLabel(labelName)
+
withLogTryResponse("deleteLabel", deleteLabelTry)
}
- def services(): List[Service] = Service.findAll()
+ def services(): List[Service] = {
+ Service.findAll()
+ }
- def serviceColumns(): List[ServiceColumn] = ServiceColumn.findAll()
+ def serviceColumns(): List[ServiceColumn] = {
+ val allServices = services().toSet
- def labels() = Label.findAll()
+ ServiceColumn
+ .findAll()
+ .filter(sc => allServices(sc.service))
+ }
+
+ def labels() = {
+ val allServiceColumns = serviceColumns().toSet
+
+ Label
+ .findAll()
+ .filter(l => allServiceColumns(l.srcColumn) || allServiceColumns(l.tgtColumn))
+ }
+
}
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
index 1275ca7..7b57059 100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/ManagementType.scala
@@ -67,7 +67,11 @@
import org.apache.s2graph.graphql.bind.Unmarshaller._
import org.apache.s2graph.graphql.types.StaticTypes._
- lazy val serviceColumnOnServiceWithPropInputObjectFields = repo.services().map { service =>
+ val services = repo.services()
+ val serviceColumns = repo.serviceColumns()
+ val labels = repo.labels()
+
+ lazy val serviceColumnOnServiceWithPropInputObjectFields = services.map { service =>
InputField(service.serviceName.toValidName, OptionInputType(InputObjectType(
s"Input_${service.serviceName.toValidName}_ServiceColumn_Props",
description = "desc here",
@@ -78,7 +82,7 @@
)))
}
- lazy val serviceColumnOnServiceInputObjectFields = repo.services().map { service =>
+ lazy val serviceColumnOnServiceInputObjectFields = services.map { service =>
InputField(service.serviceName.toValidName, OptionInputType(InputObjectType(
s"Input_${service.serviceName.toValidName}_ServiceColumn",
description = "desc here",
@@ -91,15 +95,17 @@
def makeServiceColumnEnumTypeOnService(service: Service): EnumType[String] = {
val columns = service.serviceColumns(false).toList
EnumType(
- s"Enum_${service.serviceName}_ServiceColumn",
+ s"Enum_${service.serviceName.toValidName}_ServiceColumn",
description = Option("desc here"),
- values = dummyEnum +: columns.map { column =>
- EnumValue(column.columnName.toValidName, value = column.columnName.toValidName)
- }
+ values =
+ if (columns.isEmpty) dummyEnum :: Nil
+ else columns.map { column =>
+ EnumValue(column.columnName.toValidName, value = column.columnName)
+ }
)
}
- lazy val labelPropsInputFields = repo.labels().map { label =>
+ lazy val labelPropsInputFields = labels.map { label =>
InputField(label.label.toValidName, OptionInputType(InputObjectType(
s"Input_${label.label.toValidName}_props",
description = "desc here",
@@ -134,28 +140,34 @@
lazy val ServiceListType = EnumType(
s"Enum_Service",
description = Option("desc here"),
- values =
- dummyEnum +: repo.services().map { service =>
+ values = {
+ if (services.isEmpty) dummyEnum :: Nil
+ else services.map { service =>
EnumValue(service.serviceName.toValidName, value = service.serviceName)
}
+ }
)
lazy val ServiceColumnListType = EnumType(
s"Enum_ServiceColumn",
description = Option("desc here"),
- values =
- dummyEnum +: repo.serviceColumns().map { serviceColumn =>
+ values = {
+ if (serviceColumns.isEmpty) dummyEnum :: Nil
+ else serviceColumns.map { serviceColumn =>
EnumValue(serviceColumn.columnName.toValidName, value = serviceColumn.columnName)
}
+ }
)
lazy val EnumLabelsType = EnumType(
s"Enum_Label",
description = Option("desc here"),
- values =
- dummyEnum +: repo.labels().map { label =>
+ values = {
+ if (labels.isEmpty) dummyEnum :: Nil
+ else labels.map { label =>
EnumValue(label.label.toValidName, value = label.label)
}
+ }
)
lazy val ServiceMutationResponseType = makeMutationResponseType[Service](
@@ -200,19 +212,25 @@
val AddPropServiceType = InputObjectType[ServiceColumnParam](
"Input_Service_ServiceColumn_Props",
description = "desc",
- fields = DummyInputField +: serviceColumnOnServiceWithPropInputObjectFields
+ fields =
+ if (serviceColumnOnServiceWithPropInputObjectFields.isEmpty) DummyInputField :: Nil
+ else serviceColumnOnServiceWithPropInputObjectFields
)
val ServiceColumnSelectType = InputObjectType[ServiceColumnParam](
"Input_Service_ServiceColumn",
description = "desc",
- fields = DummyInputField +: serviceColumnOnServiceInputObjectFields
+ fields =
+ if (serviceColumnOnServiceInputObjectFields.isEmpty) DummyInputField :: Nil
+ else serviceColumnOnServiceInputObjectFields
)
val InputServiceType = InputObjectType[ServiceColumnParam](
"Input_Service",
description = "desc",
- fields = DummyInputField +: serviceColumnOnServiceInputObjectFields
+ fields =
+ if (serviceColumnOnServiceInputObjectFields.isEmpty) DummyInputField :: Nil
+ else serviceColumnOnServiceInputObjectFields
)
lazy val servicesField: Field[GraphRepository, Any] = Field(
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 a18fc4e..e41e838 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
@@ -27,6 +27,7 @@
import sangria.schema._
import org.apache.s2graph.graphql.types.StaticTypes._
+import scala.collection.mutable
import scala.language.existentials
object S2Type {
@@ -102,36 +103,116 @@
labelFields.asInstanceOf[Seq[InputField[Any]]] ++ propFields.asInstanceOf[Seq[InputField[Any]]]
}
- def makeServiceColumnFields(column: ServiceColumn, allLabels: Seq[Label]): List[Field[GraphRepository, Any]] = {
- val reservedFields = List("id" -> column.columnType, "timestamp" -> "long")
+ def makeServiceColumnFields(column: ServiceColumn,
+ relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): List[Field[GraphRepository, Any]] = {
+
+ val reservedFields = Vector("id" -> column.columnType, "timestamp" -> "long")
val columnMetasKv = column.metasWithoutCache.filter(ColumnMeta.isValid).map { columnMeta => columnMeta.name -> columnMeta.dataType }
- val (sameLabel, diffLabel) = allLabels.toList.partition(l => l.srcColumn == l.tgtColumn)
+ val (sameLabel, diffLabel) = relatedLabels.toList.partition(l => l.srcColumn == l.tgtColumn)
- val outLabels = diffLabel.filter(l => column == l.srcColumn).distinct.toList
- val inLabels = diffLabel.filter(l => column == l.tgtColumn).distinct.toList
+ val outLabels = diffLabel.filter(l => column == l.srcColumn).distinct
+ val inLabels = diffLabel.filter(l => column == l.tgtColumn).distinct
val inOutLabels = sameLabel.filter(l => l.srcColumn == column && l.tgtColumn == column)
- lazy val columnFields = (reservedFields ++ columnMetasKv).map { case (k, v) => makeGraphElementField(k.toValidName, v) }
+ val columnFields = (reservedFields ++ columnMetasKv).map { case (k, v) => makeGraphElementField(k.toValidName, v) }
- 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)
+ val outLabelFields: List[Field[GraphRepository, Any]] = outLabels.map(l => toLabelFieldOnColumn("out", l, relatedLabels)(typeCache))
+ val inLabelFields: List[Field[GraphRepository, Any]] = inLabels.map(l => toLabelFieldOnColumn("in", l, relatedLabels)(typeCache))
+ val inOutLabelFields: List[Field[GraphRepository, Any]] = inOutLabels.map(l => toLabelFieldOnColumn("both", l, relatedLabels)(typeCache))
+ val propsType = wrapField(s"ServiceColumn_${column.service.serviceName.toValidName}_${column.columnName.toValidName}_props", "props", columnFields)
- lazy val labelFieldNameSet = (outLabels ++ inLabels ++ inOutLabels).map(_.label).toSet
+ val labelFieldNameSet = (outLabels ++ inLabels ++ inOutLabels).map(_.label.toValidName).toSet
- propsType :: inLabelFields ++ outLabelFields ++ inOutLabelFields ++ columnFields.filterNot(cf => labelFieldNameSet(cf.name))
+ propsType :: inLabelFields ++ outLabelFields ++ inOutLabelFields ++ columnFields.filterNot(cf => labelFieldNameSet(cf.name.toValidName))
}
- def makeServiceField(service: Service, allLabels: List[Label])(implicit repo: GraphRepository): List[Field[GraphRepository, Any]] = {
- val columnsOnService = service.serviceColumns(false).toList.map { column =>
- lazy val serviceColumnFields = makeServiceColumnFields(column, allLabels)
- lazy val ColumnType = ObjectType(
- s"ServiceColumn_${service.serviceName}_${column.columnName}",
+ def toLabelFieldOnColumn(dir: String, label: Label, relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): Field[GraphRepository, Any] = {
+
+ val LabelType = makeLabelType(dir, label, relatedLabels)(typeCache)
+
+ 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
+ }
+
+ val indexEnumType = EnumType(
+ s"Label_Index_${label.label.toValidName}",
+ description = Option("desc here"),
+ values =
+ if (label.indices.isEmpty) EnumValue("_", value = "_") :: Nil
+ else label.indices.map(idx => EnumValue(idx.name.toValidName, value = idx.name))
+ )
+
+ val paramArgs = List(
+ Argument("offset", OptionInputType(IntType), "desc here", defaultValue = 0),
+ Argument("limit", OptionInputType(IntType), "desc here", defaultValue = 100),
+ Argument("index", OptionInputType(indexEnumType), "desc here"),
+ Argument("filter", OptionInputType(StringType), "desc here")
+ )
+
+ val edgeTypeField: Field[GraphRepository, Any] = Field(
+ s"${label.label.toValidName}",
+ ListType(LabelType),
+ arguments = dirArgs ++ paramArgs,
+ description = Some("fetch edges"),
+ resolve = { c =>
+ implicit val ec = c.ctx.ec
+
+ val edgeQueryParam = graphql.types.FieldResolver.label(label, c)
+ val empty = Seq.empty[S2EdgeLike]
+
+ DeferredValue(
+ GraphRepository.edgeFetcher.deferOpt(edgeQueryParam)
+ ).map(m => m.fold(empty)(m => m._2))
+ }
+ )
+
+ edgeTypeField
+ }
+
+
+ def makeColumnType(column: ServiceColumn, relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): ObjectType[GraphRepository, Any] = {
+
+ val objectName = s"ServiceColumn_${column.service.serviceName.toValidName}_${column.columnName.toValidName}"
+
+ typeCache.getOrElseUpdate(objectName, {
+ lazy val serviceColumnFields = makeServiceColumnFields(column, relatedLabels)(typeCache)
+
+ val ColumnType = ObjectType(
+ objectName,
() => fields[GraphRepository, Any](serviceColumnFields: _*)
)
+ ColumnType
+ })
+ }
+
+ def makeServiceType(service: Service,
+ relatedColumns: Seq[ServiceColumn],
+ relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): ObjectType[GraphRepository, Any] = {
+
+ val _serviceFields = makeServiceFields(service, relatedColumns, relatedLabels)(typeCache)
+ val serviceFields = if (_serviceFields.isEmpty) DummyObjectTypeField :: _serviceFields else _serviceFields
+
+ ObjectType(
+ s"Service_${service.serviceName.toValidName}",
+ fields[GraphRepository, Any](serviceFields: _*)
+ )
+ }
+
+ def makeServiceFields(service: Service, columns: Seq[ServiceColumn], relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): List[Field[GraphRepository, Any]] = {
+
+ val columnsOnService = columns.map { column =>
+
+ val ColumnType = makeColumnType(column, relatedLabels)(typeCache)
+
Field(column.columnName.toValidName,
ListType(ColumnType),
arguments = List(
@@ -152,76 +233,52 @@
): Field[GraphRepository, Any]
}
- columnsOnService
+ columnsOnService.toList
}
- def makeLabelField(dir: String, label: Label, allLabels: Seq[Label]): Field[GraphRepository, Any] = {
+ def makeLabelType(dir: String, label: Label, relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): ObjectType[GraphRepository, Any] = {
+
+ val objectName = s"Label_${label.label.toValidName}_${dir}"
+
+ typeCache.getOrElseUpdate(objectName, {
+ lazy val labelFields = makeLabelFields(dir, label, relatedLabels)(typeCache)
+
+ val LabelType = ObjectType(
+ objectName,
+ () => fields[GraphRepository, Any](labelFields: _*)
+ )
+
+ LabelType
+ })
+ }
+
+ def makeLabelFields(dir: String, label: Label, relatedLabels: Seq[Label])
+ (typeCache: mutable.Map[String, ObjectType[GraphRepository, Any]]): List[Field[GraphRepository, Any]] = {
+
val labelReserved = List("direction" -> "string", "timestamp" -> "long")
- val labelProps = label.labelMetas.map { lm => lm.name -> lm.dataType }
+
+ val labelProps = label.labelMetas
+ .filterNot(l => labelReserved.exists(kv => kv._1 == l.name))
+ .map { lm => lm.name -> lm.dataType }
val column = if (dir == "out") label.tgtColumn else label.srcColumn
- lazy val labelFields: List[Field[GraphRepository, Any]] =
+ val labelFields: List[Field[GraphRepository, Any]] =
(labelReserved ++ labelProps).map { case (k, v) => makeGraphElementField(k.toValidName, v) }
- lazy val labelPropField = wrapField(s"Label_${label.label.toValidName}_props", "props", labelFields)
+ val labelPropField = wrapField(s"Label_${label.label.toValidName}_props", "props", labelFields)
+ val labelColumnType = makeColumnType(column, relatedLabels)(typeCache)
- lazy val labelColumnType = ObjectType(s"Label_${label.label.toValidName}_${column.columnName.toValidName}",
- () => makeServiceColumnFields(column, allLabels)
- )
-
- lazy val serviceColumnField: Field[GraphRepository, Any] = Field(column.columnName, labelColumnType, resolve = c => {
- implicit val ec = c.ctx.ec
- val vertexQueryParam = FieldResolver.serviceColumnOnLabel(c)
-
- DeferredValue(GraphRepository.vertexFetcher.defer(vertexQueryParam)).map(m => m._2.head)
- })
-
- lazy val EdgeType = ObjectType(
- s"Label_${label.label.toValidName}_${column.columnName.toValidName}_${dir}",
- () => 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
- }
-
- val idxNames = label.indices.map { idx =>
- EnumValue(idx.name.toValidName, value = idx.name.toValidName)
- }
-
- val indexEnumType = EnumType(
- s"Label_Index_${label.label.toValidName}",
- description = Option("desc here"),
- values = idxNames
- )
-
- val paramArgs = List(
- Argument("offset", OptionInputType(IntType), "desc here", defaultValue = 0),
- Argument("limit", OptionInputType(IntType), "desc here", defaultValue = 100),
- Argument("index", OptionInputType(indexEnumType), "desc here"),
- Argument("filter", OptionInputType(StringType), "desc here")
- )
-
- lazy val edgeTypeField: Field[GraphRepository, Any] = Field(
- s"${label.label.toValidName}",
- ListType(EdgeType),
- arguments = dirArgs ++ paramArgs,
- description = Some("fetch edges"),
- resolve = { c =>
+ val serviceColumnField: Field[GraphRepository, Any] =
+ Field(column.columnName.toValidName, labelColumnType, resolve = c => {
implicit val ec = c.ctx.ec
+ val vertexQueryParam = FieldResolver.serviceColumnOnLabel(c)
- val edgeQueryParam = graphql.types.FieldResolver.label(label, c)
- val empty = Seq.empty[S2EdgeLike]
+ DeferredValue(GraphRepository.vertexFetcher.defer(vertexQueryParam)).map(m => m._2.head)
+ })
- DeferredValue(GraphRepository.edgeFetcher.deferOpt(edgeQueryParam)).map(m => m.fold(empty)(_._2))
- }
- )
-
- edgeTypeField
+ List(serviceColumnField, labelPropField) ++ labelFields.filterNot(_.name.toValidName == column.columnName.toValidName)
}
}
@@ -229,26 +286,35 @@
import S2Type._
import org.apache.s2graph.graphql.bind.Unmarshaller._
-
- implicit val graphRepository = repo
+ import scala.collection._
/**
* fields
*/
- lazy val serviceFields: List[Field[GraphRepository, Any]] = repo.services().map { service =>
- lazy val serviceFields = DummyObjectTypeField :: makeServiceField(service, repo.labels())
+ val serviceFields: List[Field[GraphRepository, Any]] = {
+ val allColumns = repo.serviceColumns()
+ val allLabels = repo.labels()
- lazy val ServiceType = ObjectType(
- s"Service_${service.serviceName.toValidName}",
- fields[GraphRepository, Any](serviceFields: _*)
- )
+ val typeCache = mutable.Map.empty[String, ObjectType[GraphRepository, Any]]
- Field(
- service.serviceName.toValidName,
- ServiceType,
- description = Some(s"serviceName: ${service.serviceName}"),
- resolve = _ => service
- ): Field[GraphRepository, Any]
+ repo.services().flatMap { service =>
+ val relatedColumns = allColumns.filter(_.service == service).toSet
+ val relatedLabels = allLabels.filter(l => relatedColumns(l.srcColumn) || relatedColumns(l.tgtColumn))
+
+ if (relatedColumns.isEmpty) Nil
+ else {
+ val ServiceType = makeServiceType(service, relatedColumns.toVector, relatedLabels.distinct)(typeCache)
+
+ val f = Field(
+ service.serviceName.toValidName,
+ ServiceType,
+ description = Some(s"serviceName: ${service.serviceName}"),
+ resolve = _ => service
+ ): Field[GraphRepository, Any]
+
+ List(f)
+ }
+ }
}
/**
@@ -287,7 +353,7 @@
* Provide s2graph query / mutate API
* - Fields is created(or changed) for metadata is changed.
*/
- lazy val queryFields = serviceFields
+ val queryFields = serviceFields
lazy val mutationFields: List[Field[GraphRepository, Any]] = List(
Field("addVertex",
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 7451023..d094ee0 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
@@ -26,7 +26,7 @@
*
* When a Label or Service is created, the GraphQL schema is created dynamically.
*/
-class SchemaDef(g: GraphRepository) {
+class SchemaDef(g: GraphRepository, withAdmin: Boolean = false) {
import sangria.schema._
@@ -39,17 +39,24 @@
fields(s2Type.queryFields ++ queryManagementFields: _*)
)
- val mutateManagementFields = List(wrapField("MutationManagement", "Management", s2ManagementType.mutationFields))
- val S2MutationType = ObjectType[GraphRepository, Any](
- "Mutation",
- fields(s2Type.mutationFields ++ mutateManagementFields: _*)
- )
+ lazy val mutateManagementFields = List(wrapField("MutationManagement", "Management", s2ManagementType.mutationFields))
+
+ val S2MutationType =
+ if (!withAdmin) None
+ else {
+ val mutationTpe = ObjectType[GraphRepository, Any](
+ "Mutation",
+ fields(s2Type.mutationFields ++ mutateManagementFields: _*)
+ )
+
+ Option(mutationTpe)
+ }
val directives = S2Directive.Transform :: BuiltinDirectives
private val s2Schema = Schema(
S2QueryType,
- Option(S2MutationType),
+ S2MutationType,
directives = directives
)