///|
/// The schema the showcase page runs: a small star catalogue, small enough that
/// the whole pipeline — parse, validate, execute — answers while the reader is
/// still looking at the editor, and deep enough to show a computed field.
///
/// This package exists for the documentation site only: it links the library
/// into JavaScript and exposes two names the page calls. It is not part of the
/// library's public surface.
///|
fn catalogue() -> Json {
let sirius : Json = {
"id": "sirius",
"name": "Sirius",
"magnitude": -1.46,
"constellation": "Canis Major",
}
let vega : Json = {
"id": "vega",
"name": "Vega",
"magnitude": 0.03,
"constellation": "Lyra",
}
let polaris : Json = {
"id": "polaris",
"name": "Polaris",
"magnitude": 1.98,
"constellation": "Ursa Minor",
}
{
"name": "The northern sky",
"stars": [sirius, vega, polaris],
"brightest": sirius,
}
}
///|
/// The demo schema: a query root that hands out the catalogue, plus a field the
/// resolver computes from the parent instead of reading it off the object.
fn demo() -> (@moongql.Schema, @moongql.Resolvers) {
let schema = @moongql.Schema::new()
let query = schema.object("Query")
query.field("sky", NonNull(Named("Sky")))
// A named tuple gives the argument's type, which is what lets `NonNull` and
// `Scalar` resolve without naming the package they live in.
let by_id : (String, @moongql.GqlType) = ("id", NonNull(Scalar("ID")))
query.field_args("star", [by_id], Named("Star"))
let sky = schema.object("Sky")
sky.field("name", NonNull(Scalar("String")))
sky.field("stars", NonNull(ListOf(NonNull(Named("Star")))))
sky.field("brightest", Named("Star"))
let star = schema.object("Star")
star.field("id", NonNull(Scalar("ID")))
star.field("name", NonNull(Scalar("String")))
star.field("magnitude", NonNull(Scalar("Float")))
star.field("constellation", Scalar("String"))
star.field("label", NonNull(Scalar("String")))
let resolvers = @moongql.Resolvers::new()
resolvers.field("Query", "sky", fn(_info) { catalogue() })
resolvers.field("Query", "star", fn(info) {
let wanted = match info.arg("id") {
String(id) => id
_ => ""
}
let stars = match catalogue() {
Object(fields) =>
match fields.get("stars") {
Some(Array(items)) => items
_ => []
}
_ => []
}
let mut found = Json::null()
for item in stars {
if found is Null {
match item {
Object(fields) =>
match fields.get("id") {
Some(String(id)) => if id == wanted { found = item }
_ => ()
}
_ => ()
}
}
}
found
})
// A field with no stored value: the resolver composes it from the parent,
// which is how a row becomes the shape the client asked for.
resolvers.field("Star", "label", fn(info) {
let name = match info.parent {
Object(fields) =>
match fields.get("name") {
Some(String(name)) => name
_ => ""
}
_ => ""
}
let constellation = match info.parent {
Object(fields) =>
match fields.get("constellation") {
Some(String(c)) => c
_ => ""
}
_ => ""
}
if constellation == "" {
Json(name)
} else {
Json("\{name} of \{constellation}")
}
})
(schema, resolvers)
}
///|
/// The demo schema as SDL, so the page can show the schema it is querying.
pub fn sdl() -> String {
let (schema, _) = demo()
schema.to_sdl()
}
///|
/// Run a document against the demo schema and return the response as JSON text.
/// A bad document comes back as `{"errors": [...]}` rather than an exception,
/// which is exactly what the page needs to display.
pub fn run(document : String) -> String {
let (schema, resolvers) = demo()
@moongql.execute(schema, resolvers, document).stringify()
}