# Scalars
#
# As expected by the name, a scalar represents a primitive value in GraphQL.
# GraphQL responses take the form of a hierarchical tree; the leaves on these
# trees are GraphQL scalars.
#
# All GraphQL scalars are representable as strings, though depending on the
# response format being used, there may be a more appropriate primitive for the
# given scalar type, and server should use those types when appropriate.
#
# GraphQL provides a number of built-in scalars, but type systems can add
# additional scalars with semantic meaning. For example, a GraphQL system could
# define a scalar called Time which, while serialized as a string, promises to
# conform to ISO-8601. When querying a field of type Time, you can then rely on
# the ability to parse the result with an ISO-8601 parser and use a
# client-specific primitive for time. Another example of a potentially useful
# custom scalar is Url, which serializes as a string, but is guaranteed by the
# server to be a valid URL.
#
# A server may omit any of the built-in scalars from its schema, for example if
# a schema does not refer to a floating-point number, then it will not include
# the Float type. However, if a schema includes a type with the name of one of
# the types described here, it must adhere to the behavior described. As an
# example, a server must not include a type called Int and use it to represent
# 128-bit numbers, or internationalization information.
#
# Result Coercion
#
# A GraphQL server, when preparing a field of a given scalar type, must uphold
# the contract the scalar type describes, either by coercing the value or
# producing an error.
#
# For example, a GraphQL server could be preparing a field with the scalar type
# Int and encounter a floating-point number. Since the server must not break the
# contract by yielding a non-integer, the server should truncate the fractional
# value and only yield the integer value. If the server encountered a boolean
# true value, it should return 1. If the server encountered a string, it may
# attempt to parse the string for a base-10 integer value. If the server
# encounters some value that cannot be reasonably coerced to an Int, then it
# must raise a field error.
#
# Since this coercion behavior is not observable to clients of the GraphQL
# server, the precise rules of coercion are left to the implementation. The only
# requirement is that the server must yield values which adhere to the expected
# Scalar type.
#
# Input Coercion
#
# If a GraphQL server expects a scalar type as input to an argument, coercion is
# observable and the rules must be well defined. If an input value does not
# match a coercion rule, a query error must be raised.
#
# GraphQL has different constant literals to represent integer and
# floating-point input values, and coercion rules may apply differently
# depending on which type of input value is encountered. GraphQL may be
# parameterized by query variables, the values of which are often serialized
# when sent over a transport like HTTP. Since some common serializations (ex.
# JSON) do not discriminate between integer and floating-point values, they are
# interpreted as an integer input value if they have an empty fractional part
# (ex. 1.0) and otherwise as floating-point input value.
#
# For all types below, with the exception of Non-Null, if the explicit value
# null is provided, then the result of input coercion is null.
#' Parse AST
#'
#' This is a helper function for Scalars. Given a particular kind and a resolve
#' function, it produces a function that will only parse values of a particular
#' kind.
#'
#' Typically, \code{kind} is the same as the class of the Scalar. When making a
#' new Scalar, parse_ast defaults to use the name of the scalar and the scalar's
#' parse value function.
#'
#' This function should only need to be used when defining a schema in
#' \code{\link{gqlr_schema}()}
#'
#' @param kind single character name of a class to parse
#' @param resolve function to parse the value if the kind is correct
#' @return function that takes \code{obj} and \code{schema} that will only parse
#' the value if the \code{kind} is inherited in the \code{obj}
#' @export
#' @examples
#' parse_date_value <- function(obj, schema) {
#' as.Date(obj)
#' }
#' parse_ast("Date", parse_date_value)
#'
#' # Example from Int scalar
#' parse_int <- function(value, ...) {
#' MAX_INT <- 2147483647
#' MIN_INT <- -2147483648
#' num <- suppressWarnings(as.integer(value))
#' if (!is.na(num)) {
#' if (num <= MAX_INT && num >= MIN_INT) {
#' return(num)
#' }
#' }
#' return(NULL)
#' }
#' parse_ast("IntValue", parse_int)
parse_ast <- function(kind, resolve) {
fn <- function(obj, schema) {
if (inherits(obj, kind)) {
resolve(obj$value, schema)
} else {
NULL
}
}
pryr_unenclose(fn)
}
for_onload(function() {
coerce_int <- function(value, ...) {
MAX_INT <- 2147483647
MIN_INT <- -2147483648
num <- suppressWarnings(as.integer(value))
if (!is.na(num)) {
if (num <= MAX_INT && num >= MIN_INT) {
return(num)
}
}
return(NULL)
}
Int <- ScalarTypeDefinition$new(
name = Name$new(value = "Int"),
description = paste0(
"The Int scalar type represents a signed 32-bit numeric non-fractional value. ",
"Response formats that support a 32-bit integer or a number type should use that ",
"type to represent this scalar."
),
.resolve = coerce_int,
.parse_ast = parse_ast("IntValue", coerce_int)
)
coerce_float <- function(value, ...) {
num <- suppressWarnings(as.numeric(value))
if (is.numeric(num)) {
return(num)
} else {
return(NULL)
}
}
Float <- ScalarTypeDefinition$new(
name = Name$new(value = "Float"),
description = collapse(
"The `Float` scalar type represents signed double-precision fractional ",
"values as specified by ",
"[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."
),
.resolve = coerce_float,
.parse_ast = pryr_unenclose(function(obj, schema) {
if (
inherits(obj, "IntValue") ||
inherits(obj, "FloatValue")
) {
coerce_float(obj$value, schema)
} else {
NULL
}
})
)
coerce_string <- function(value, ...) {
char <- suppressWarnings(as.character(value))
if (is.character(char)) {
return(char)
} else {
return(NULL)
}
}
String <- ScalarTypeDefinition$new(
name = Name$new(value = "String"),
description = collapse(
"The `String` scalar type represents textual data, represented as UTF-8 ",
"character sequences. The String type is most often used by GraphQL to ",
"represent free-form human-readable text."
),
.resolve = coerce_string,
.parse_ast = parse_ast("StringValue", coerce_string)
)
coerce_boolean <- function(value, ...) {
val <- suppressWarnings(as.logical(value))
if (is.logical(val)) {
if (!is_nullish(val)) {
return(val)
}
}
return(NULL)
}
Boolean <- ScalarTypeDefinition$new(
name = Name$new(value = "Boolean"),
description = "The `Boolean` scalar type represents `TRUE` or `FALSE`.",
.resolve = coerce_boolean,
.parse_ast = parse_ast("BooleanValue", coerce_boolean)
)
# nolint start
## Not including in R setup
# # no literal AST definition, but defining as such
# ID = ScalarTypeDefinition$new(
# name = Name$new(value = "ID"),
# description = collapse(
# "The `ID` scalar type represents a unique identifier, often used to ",
# "refetch an object or as key for a cache. The ID type appears in a JSON ",
# "response as a String; however, it is not intended to be human-readable. ",
# "When expected as an input type, any string (such as `"4"`) or integer ",
# "(such as `4`) input value will be accepted as an ID."
# ),
# .resolve = as.character,
# .parse_ast = function(astObj) {
# if (
# inherits(astObj, "String") ||
# inherits(astObj, "Int")
# ) {
# return(as.character(astObj$value))
# } else {
# return(NULL)
# }
# }
# )
# nolint end
}) # end for_onload
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.