moonschema

    moonschema — JSON Schema validation for MoonBit: draft-04, draft-06, draft-07, 2019-09 and 2020-12, measured against every case of the official JSON-Schema-Test-Suite. It validates; it does not parse JSON — that is moonjson — and it opens no sockets.

    json-schema
    schema
    validation
    openapi
    json
    moonbit
    Download zip
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    5 days ago
    Downloads
    238

    #moonschema

    JSON Schema validation for MoonBit. It validates; it does not parse JSON — a caller hands in a Json tree, which moonjson reads.

    let schema = @moonschema.Schema::new(document) // `document` is a Json tree
    schema.valid(instance) // the flag output: yes or no
    schema.faults(instance) // the basic output: every reason

    // A reference to another document reaches what the caller hands it, and nothing
    // else: this opens no sockets.
    @moonschema.Schema::new(document, remotes={ "https://example.test/int": other })

    Run moon run examples/tour for a worked example.

    #What it passes

    Every required case of the official suite, for all five versions:

    VersionCasesPassing
    draft-04618618
    draft-06841841
    draft-07929929
    2019-0912611261
    2020-1213011301
    total49504950

    Nothing is skipped and nothing is excused: the gate fails if a single case stops passing, and it fails just as loudly if a case listed as known-failing starts passing, because the list would then be describing something that is no longer so. The list is empty.

    The counts are the cases the suite calls required. The optional/ directory — format assertions, big numbers beyond a double, non-BMP text — is not run, by the same reading of the specification that makes format an annotation.

    #Versions

    Five released versions, all of them still in use:

    ValueVersionWhy it is here
    Draft4draft-04 (2013)What OpenAPI 3.0 profiles, and what much Java and Python tooling still emits
    Draft6draft-06 (2017)$id replaces id; exclusiveMinimum becomes a number
    Draft7draft-07 (2018)The most widely deployed version
    Draft20192019-09Vocabularies, $defs, $recursiveRef, unevaluatedProperties
    Draft20202020-12$dynamicRef, prefixItems — and what OpenAPI 3.1 is

    draft-03 is not here and will not be: it predates the $ keywords and nothing has been written against it since 2010. The next release arrives as another value of this enum and a keyword table, not as a second validator.

    A document is read at the version it names. $schema wins over the draft argument, which wins over the preset — the same "more specific wins" rule the rest of this family publishes. A $schema URI this does not know answers None rather than a guess: a document written against something else is not a document to read at a version of our choosing.

    Both http and https spellings are accepted, with or without the trailing #, because both are found in the wild.

    #What it is measured against

    The official test suite is the suite/ submodule and the acceptance gate — every required case, not a selection. It is run by the gate/ module, which reads the suite off disk, feeds the validator the remote documents the suite refers to, and fails unless the result is exactly the table above. The library itself reads no files: the gate is where the caller lives.

    git submodule update --init --recursive cd gate && moon run . --target native

    The specification is the spec/ submodule. Neither submodule, and not the gate, ships in the package: .moonignore excludes them, because a consumer installing this wants the validator and not the paperwork.

    #What it will not do

    Fetch a remote $ref. A validator should not open sockets. The seam is remotes: a map from URI to document, which the caller fills from wherever it keeps them. What is not handed over does not resolve, and a reference that resolves to nothing is a fault rather than a silent pass.

    Assert format. The specification makes format an annotation unless a caller asks otherwise, and this does not offer the otherwise yet.

    Hyper-schema. That is a different specification.

    Compile schemas to code. Interpretation first; if it measures slow, that is the time to argue about it.

    #Install

    moon add moonbitstack/moonschema

    #Licence

    Apache-2.0.

    Refused

    pub(all) suberror Refused {
    Unknown(String)
    Broken(at~ : String, why~ : String)
    } derive(Eq,
    Debug
    )

    Why a schema could not be read.

    These are faults in the schema, not in the instance an instance that fails validation produces [Fault]s instead.
    impl Show for Refused

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::output

    fn Refused::output(self : Refused, logger : &Logger) -> Unit

    Refused::to_repr

    Refused::to_string

    fn Refused::to_string(self : Refused) -> String

    Draft

    pub(all) enum Draft {
    Draft4
    Draft6
    Draft7
    Draft2019
    Draft2020
    } derive(Eq,
    Debug
    )

    Which version of JSON Schema a document is written against.

    The five released versions that are still in use. draft-03 is not here: it predates the $ keywords and nothing has been written against it since 2010. The next release is not here yet either; it arrives as another value of this enum and a keyword table, not as a second validator.
    impl Show for Draft

    Draft::equal

    fn Draft::equal(Draft, Draft) -> Bool

    Draft::not_equal

    fn Draft::not_equal(x : Draft, y : Draft) -> Bool

    Draft::of

    fn Draft::of(uri : StringView) -> Draft?

    The version a $schema URI names.

    It answers None for a URI it does not know rather than falling back to a version of its own choosing: a document written against something else is not a document to guess at. Both the http and https spellings are accepted, and a trailing # is optional, because both are found in the wild.

    Draft::output

    fn Draft::output(self : Draft, logger : &Logger) -> Unit

    Draft::to_repr

    Draft::to_string

    fn Draft::to_string(self : Draft) -> String

    Draft::uri

    fn Draft::uri(self : Draft) -> String

    The $schema URI that names this version.

    Fault

    pub(all) struct Fault {
    at : String
    schema_at : String
    keyword : String
    message : String
    } derive(Eq,
    Debug
    )

    One reason an instance failed, in the shape the basic output gives it (2020-12 §12.4.2).
    impl Show for Fault

    Fault::equal

    fn Fault::equal(Fault, Fault) -> Bool

    Fault::not_equal

    fn Fault::not_equal(x : Fault, y : Fault) -> Bool

    Fault::output

    fn Fault::output(self : Fault, logger : &Logger) -> Unit

    Fault::to_repr

    Fault::to_string

    fn Fault::to_string(self : Fault) -> String

    Output

    pub(all) enum Output {
    Flag
    Basic
    Detailed
    Verbose
    } derive(Eq,
    Debug
    )

    How much a validation says when it fails.

    The four formats the specification itself defines (2020-12 §12.4), named as it names them.
    impl Show for Output

    Output::equal

    fn Output::equal(Output, Output) -> Bool

    Output::not_equal

    fn Output::not_equal(x : Output, y : Output) -> Bool

    Output::output

    fn Output::output(self : Output, logger : &Logger) -> Unit

    Output::to_repr

    Output::to_string

    fn Output::to_string(self : Output) -> String

    Schema

    pub struct Schema {
    // private fields
    }

    A schema document, read and ready to validate instances against.

    The version is the one the document names in $schema, whatever draft said; a document is more specific than a setting. remotes are the documents a $ref may reach outside this one, by the URI each is registered under: this library opens no sockets, so what is not fed to it does not exist.

    Schema::faults

    fn Schema::faults(self : Schema, instance : Json) -> Array[Fault]

    Every reason instance failed, or an empty array when it validated.

    This is the basic output (§12.4.2), flattened: each fault says where in the instance it was, which keyword refused it, and where that keyword was.

    Schema::new

    fn Schema::new(document : Json, draft? : Draft, remotes? : Map[String, Json]) -> Schema raise Refused

    Read a schema document.

    draft is the version to read it at when it does not name one; a $schema it does name wins. remotes are the documents a $ref may reach, each under the URI it is referred to by.

    Schema::valid

    fn Schema::valid(self : Schema, instance : Json) -> Bool

    Whether instance validates.

    This is the flag output (§12.4.1): the cheapest answer, and the one a guard on a request wants. [Schema::faults] says why not.

    draft

    let draft : Draft

    The version assumed when a document does not name one.

    2020-12: the current release, and the one OpenAPI 3.1 is defined against. A document that says $schema is read at the version it says, whatever this is set to — the document is more specific than the setting.

    output

    let output : Output

    The output format a validation produces unless asked for another.

    Flag, because the common case is a guard that wants a yes or a no and collecting errors nobody reads is work nobody asked for. A caller that wants to tell someone what went wrong asks for Basic.

    unicode

    let unicode : String

    The Unicode version these ranges are from.