cuelang / cue

CUE has moved to https://github.com/cue-lang/cue
https://cuelang.org
Apache License 2.0
3.09k stars 171 forks source link

Validate JSON Data against a set of cue definitions in Go #1031

Closed owulveryck closed 3 years ago

owulveryck commented 3 years ago

This is an issue following a comment orignially posted by @owulveryck in https://github.com/cuelang/cue/discussions/1030#discussioncomment-807569_

Consider the following data:

import (
    "github.com/mycorp/myproject/producers/sample1:sample1"
)

// producers/sample1/sample.cue
message: #Schema & {
    envelop: {
        type:    "net.mycompany.sample1:c"
        subject: "123e4567-e89b-12d3-a456-426614174000"
    }
    data: {
        // weight is the weight in kgs
        weight: 92
    }
}

I can validate the file without explicitly loading the definitions:

...
    binst := load.Instances([]string{ "testdata/sample.cue"}, nil)
...

I wonder now if there is a way to load the "definitions" in a context, a runtime, or whatever, so I can validate the data on the fly in a webservice for example?

edit

I tried with JSON which would do what I am trying to achieve:

package main

import (
    "io/ioutil"
    "log"

    "cuelang.org/go/cue/cuecontext"
    "cuelang.org/go/cue/load"
    "cuelang.org/go/encoding/json"
)

func main() {
    ctx := cuecontext.New()

    binst := load.Instances([]string{"definitions.cue"}, nil)
    insts, err := ctx.BuildInstances(binst)
    if err != nil {
        log.Fatal(err)
    }
    if len(insts) != 1 {
        log.Fatal("more than one instance")
    }
    v := insts[0]
    b, err := ioutil.ReadFile("testdata/bad.json")
    if err != nil {
        log.Fatal(err)
    }
    err = json.Validate(b, v)
    if err == nil {
        log.Fatal(err)

    }
}

and bad.json is:

{
    "message": {
        "envelop": {
            "topic": "net.mycompany.sample1.topic:1234",
            "subject": "123e4567-e89b-12d3-a456-426614174000",
            "type": "net.mycompany.sample1:c",
            "version": "v1.0.0"
        },
        "data": {
            "weight": 250
        }
    }
}

I think that this should fail, but it does not :(

owulveryck commented 3 years ago

I digged a bit into the json.Validate method, and wrote a more straightforward version:

package main

import (
    "io/ioutil"
    "log"

    "cuelang.org/go/cue"
    "cuelang.org/go/cue/cuecontext"
    "cuelang.org/go/cue/load"
)

func main() {
    ctx := cuecontext.New()

    buildInstances := load.Instances([]string{"definitions.cue"}, nil)
    values, err := ctx.BuildInstances(buildInstances)
    if err != nil {
        log.Fatal(err)
    }
    if len(values) != 1 {
        log.Fatal("more than one value")
    }
    v := values[0]

    b, err := ioutil.ReadFile("testdata/bad.json")
    if err != nil {
        log.Fatal(err)
    }
    data := ctx.CompileBytes(b)
    unified := v.Unify(data)
    opts := []cue.Option{
        cue.Attributes(true),
        cue.Definitions(true),
        cue.Hidden(true),
    }
    err = unified.Validate(opts...)
    if err != nil {
        log.Fatal(err)
    }
}

I think this is the way to go (according to the discussion https://github.com/cuelang/cue/discussions/810). But it still does not work. I don't understand this comment

Value v and w must be obtained from the same build. TODO: remove this requirement.

Maybe it is the reason it does not work as expected.

owulveryck commented 3 years ago

This simply cannot work as my JSON "message" does not handle the type; therefore there is no way to validate this.

Looking for a proper way to add it on runtime, but so far, this issue is not relevant anymore

myitcv commented 3 years ago

Please also see my response in https://github.com/cuelang/cue/discussions/1030#discussioncomment-813968

cueckoo commented 3 years ago

This issue has been migrated to https://github.com/cue-lang/cue/issues/1031.

For more details about CUE's migration to a new home, please see https://github.com/cue-lang/cue/issues/1078.