next.models.graphql.scala Maven / Gradle / Ivy
package otoroshi.next.models
import otoroshi.utils.syntax.implicits.BetterJsValue
import play.api.libs.json.{
Format,
JsArray,
JsBoolean,
JsError,
JsNull,
JsNumber,
JsObject,
JsString,
JsSuccess,
JsValue,
Json
}
import sangria.ast.{
Argument,
BigDecimalValue,
BigIntValue,
BooleanValue,
Directive,
Document,
FieldDefinition,
FloatValue,
InputValueDefinition,
IntValue,
InterfaceTypeDefinition,
ListType,
ListValue,
NamedType,
NotNullType,
NullValue,
ObjectTypeDefinition,
StringValue,
TypeDefinition,
TypeSystemDefinition,
Value
}
import sangria.schema.Schema
import scala.util.Try
object GraphQLFormats {
def jsonToArgumentValue(value: JsValue, isJsonDirectiveArgument: Boolean): Value = value match {
case JsNull => NullValue()
case value: JsBoolean => BooleanValue(value.value)
case JsNumber(value) => BigDecimalValue(value)
case JsString(value) => StringValue(value)
case o: JsArray =>
if (isJsonDirectiveArgument) StringValue(Json.stringify(o))
else
ListValue(values =
o.value.map(arg => jsonToArgumentValue(arg, isJsonDirectiveArgument)).toVector
) // TODO - manage ListValue recursively
case o: JsObject => StringValue(Json.stringify(o))
case _ => StringValue("")
}
def argumentValueToJson(value: Value): JsValue = value match {
case IntValue(value, _, _) => JsNumber(value)
case BigIntValue(value, _, _) => JsNumber(value.intValue())
case FloatValue(value, _, _) => JsNumber(value)
case BigDecimalValue(value, _, _) => JsNumber(value)
case StringValue(value, _, _, _, _) => JsString(value)
case BooleanValue(value, _, _) => JsBoolean(value)
case ListValue(values, _, _) => JsArray(values.map(argumentValueToJson))
case _ => JsString("")
}
def fieldDefinitionFmt =
new Format[FieldDefinition] {
override def writes(field: FieldDefinition) =
Json.obj(
"name" -> field.name,
"fieldType" -> Json.obj(
"type" -> field.fieldType.namedType.name,
"isList" -> field.fieldType.isInstanceOf[ListType],
"required" -> field.fieldType.isInstanceOf[NotNullType]
),
"arguments" -> field.arguments.map(f => {
Json.obj(
"name" -> f.name,
"valueType" -> Json.obj(
"type" -> f.valueType.namedType.name,
"isList" -> f.valueType.isInstanceOf[ListType],
"required" -> f.valueType.isInstanceOf[NotNullType]
)
// TODO - manage defaultValue
)
}),
"directives" -> field.directives.map(directive =>
Json.obj(
"name" -> directive.name,
"arguments" -> directive.arguments.map(argument =>
Json.obj(
"name" -> argument.name,
"value" -> argumentValueToJson(argument.value)
)
)
)
)
)
override def reads(json: JsValue) = {
val field = json.asOpt[JsObject].getOrElse(Json.obj())
val fieldType = field.select("fieldType").as[JsObject]
val `type` = fieldType.select("type").as[String]
val isList = fieldType.select("isList").asOpt[Boolean].getOrElse(false)
val requiredField = fieldType.select("required").asOpt[Boolean].getOrElse(false)
val fullFieldType = if (requiredField) NotNullType(NamedType(`type`)) else NamedType(`type`)
val directives = field.select("directives").as[JsArray].value.map(_.as[JsObject])
Try {
JsSuccess(
FieldDefinition(
name = (json \ "name").as[String],
fieldType = if (isList) ListType(fullFieldType) else fullFieldType,
arguments = field
.select("arguments")
.asOpt[JsArray]
.getOrElse(Json.arr())
.value
.map(_.as[JsObject])
.map(argument => {
val valueType = argument.select("valueType").as[JsObject]
val argumentType = valueType.select("type").as[String]
val argumentIsList = valueType.select("isList").as[Boolean]
val required = valueType.select("required").as[Boolean]
val `type` = if (required) NotNullType(NamedType(argumentType)) else NamedType(argumentType)
InputValueDefinition(
name = argument.select("name").as[String],
valueType = if (argumentIsList) ListType(`type`) else `type`,
defaultValue = None // TODO - manage defaultValue
)
})
.toVector,
directives = directives
.map(directive => {
val directiveName = directive.select("name").as[String]
Directive(
name = directiveName,
arguments = directive
.select("arguments")
.as[JsArray]
.value
.map(_.as[JsObject])
.map(argument => {
val name = argument.keys.head
Argument(
name = name,
value = jsonToArgumentValue(
argument.values.head,
isJsonDirectiveArgument = name == "data" && directiveName == "json"
)
)
})
.toVector
)
})
.toVector
)
)
} recover { case e =>
JsError(e.getMessage)
} get
}
}
def objectTypeDefinitionFmt =
new Format[ObjectTypeDefinition] {
override def writes(obj: ObjectTypeDefinition) =
Json.obj(
"name" -> obj.name,
"fields" -> obj.fields.map(fieldDefinitionFmt.writes),
"directives" -> obj.directives.map(directive =>
Json.obj(
"name" -> directive.name,
"arguments" -> directive.arguments.map(argument =>
Json.obj(
"name" -> argument.name,
"value" -> argumentValueToJson(argument.value)
)
)
)
)
)
override def reads(json: JsValue) =
Try {
JsSuccess(
ObjectTypeDefinition(
name = json.select("name").as[String],
interfaces = Vector.empty,
fields = json
.select("fields")
.asOpt[JsArray]
.getOrElse(Json.arr())
.value
.map(_.as[JsObject])
.map(fieldDefinitionFmt.reads)
.flatMap {
case JsSuccess(v, _) => Some(v)
case JsError(_) => None
}
.toVector
)
)
} recover { case e =>
JsError(e.getMessage)
} get
}
def interfaceTypeDefinitionFmt =
new Format[InterfaceTypeDefinition] {
override def writes(obj: InterfaceTypeDefinition) =
Json.obj(
"name" -> JsString(obj.name),
"fields" -> obj.fields.map(fieldDefinitionFmt.writes),
"directives" -> obj.directives.map(directive =>
Json.obj(
"name" -> directive.name,
"arguments" -> directive.arguments.map(argument =>
Json.obj(
"name" -> argument.name,
"value" -> argument.value.toString()
)
)
)
)
)
override def reads(json: JsValue) =
Try {
JsSuccess(
InterfaceTypeDefinition(
name = json.select("name").as[String],
fields = json
.select("fields")
.asOpt[JsArray]
.getOrElse(Json.arr())
.value
.map(_.as[JsObject])
.map(fieldDefinitionFmt.reads)
.flatMap {
case JsSuccess(v, _) => Some(v)
case JsError(_) => None
}
.toVector
)
)
} recover { case e =>
JsError(e.getMessage)
} get
}
def astDocumentToJson(document: Document) = document.definitions.map {
case definition: TypeSystemDefinition =>
definition match {
case definition: TypeDefinition =>
definition match {
case obj: ObjectTypeDefinition => objectTypeDefinitionFmt.writes(obj)
case i: InterfaceTypeDefinition => interfaceTypeDefinitionFmt.writes(i)
case _ => Json.obj()
}
case _ => Json.obj()
}
case _ => Json.obj()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy