Skip to content

Commit

Permalink
docs/howto: validate JSON using the Go API
Browse files Browse the repository at this point in the history
This adds a guide demonstrating how to validate JSON data files against
an embedded CUE schema.

A YAML counterpart is requested in cue-lang/docs-and-content#150, as it
was discovered during this change that the YAML equivalent couldn't be
created simply by performing "s/json/yaml/g" on the source.

Preview-Path: /docs/howto/validate-json-using-go-api/
Signed-off-by: Paul Jolly <[email protected]>
Change-Id: I53c3112650f36a32b86a5d89f670f738d1b798c8
  • Loading branch information
myitcv authored and jpluscplusm committed Jun 3, 2024
1 parent 4c3195c commit eac6463
Show file tree
Hide file tree
Showing 4 changed files with 442 additions and 0 deletions.
169 changes: 169 additions & 0 deletions content/docs/howto/validate-json-using-go-api/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
title: Validate JSON using the Go API
tags:
- go api
- validation
- encodings
authors:
- myitcv
toc_hide: true
---

This guide demonstrates how to write a Go program that validates JSON files
using an embeded CUE schema.

{{{with _script_ "en" "set caches to speed up re-running"}}}
export GOMODCACHE=/caches/gomodcache
export GOCACHE=/caches/gobuild
{{{end}}}

## Set up some schema and data files

{{{with step}}}
Create a CUE schema.

You can use any schema that's relevant to your specific data, but our example uses this simple CUE:

{{{with upload "en" "cue schema"}}}
-- schema.cue --
#Schema: {
name?: string
age?: int
}
{{{end}}}
{{{end}}}


{{{with step}}}
Create some known-good and known-bad test data.

You may already have some representative test data. This data is relevant to our example schema:

{{{with upload "en" "good data"}}}
-- good.json --
{
"name": "Charlie Cartwright",
"age": 80
}
{{{end}}}

{{{with upload "en" "bad data"}}}
-- bad.json --
{
"name": [
"Moby",
"Dick"
],
"age": "173"
}
{{{end}}}
{{{end}}}

{{{with step}}}
Verify that your schema accepts and rejects your test data appropriately.

Our example schema is contained in the `#Schema` definition, which we reference explicitly:

{{{with script "en" "test schema"}}}
cue vet schema.cue good.json -d '#Schema'
! cue vet schema.cue bad.json -d '#Schema'
{{{end}}}
{{{end}}}

## Create a Go program

{{{with step}}}
Initialize a Go module, or use an existing one if that's more suitable for your situation:

{{{with script "en" "go mod init"}}}
#ellipsis 0
go mod init go.example
{{{end}}}
{{{end}}}

{{{with step}}}
Create a main program.

You can use this example code as a starting point:

{{{with upload "en" "main go"}}}
-- main.go --
package main

import (
"fmt"
"log"
"os"

_ "embed"

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

// Embed our schema in a Go string variable.
//
//go:embed schema.cue
var cueSource string

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

// Build the schema
schema := ctx.CompileString(cueSource).LookupPath(cue.ParsePath("#Schema"))

// Load the JSON file specified (the program's sole argument) as a CUE value
dataFilename := os.Args[1]
dataFile, err := os.ReadFile(dataFilename)
if err != nil {
log.Fatal(err)
}
dataExpr, err := json.Extract(dataFilename, dataFile)
if err != nil {
log.Fatal(err)
}
dataAsCUE := ctx.BuildExpr(dataExpr)

// Validate the JSON data using the schema
unified := schema.Unify(dataAsCUE)
if err := unified.Validate(); err != nil {
fmt.Println("❌ JSON: NOT ok")
log.Fatal(err)
}

fmt.Println("✅ JSON: ok")
}
{{{end}}}

This example code embeds the CUE from `schema.cue` and uses it to validate a
single JSON file, printing the validation result to its standard output stream.
{{{end}}}

{{{with step}}}
Add a dependency on `cuelang.org/go` and ensure the Go module is tidy:
{{{with script "en" "deps and tidy"}}}
#ellipsis 0
go get cuelang.org/go@${CUELANG_CUE_LATEST}
#ellipsis 0
go mod tidy
{{{end}}}
{{{end}}}

## Run the Go program

{{{with step}}}
Verify that the Go program accepts and rejects your test data as expected:

{{{with script "en" "verify go program"}}}
go run . good.json
! go run . bad.json
{{{end}}}
{{{end}}}

## Related content

- Go API: [`cue`](https://pkg.go.dev/cuelang.org/go/cue#section-documentation) -- package documentation
- Go API: [`cue/cuecontext`](https://pkg.go.dev/cuelang.org/go/cue/cuecontext#section-documentation) -- package documentation
- Go API: [`encoding/json`](https://pkg.go.dev/cuelang.org/go/encoding/json#section-documentation) -- package documentation
- Tag: {{< tag "go api" >}} -- pages explaining and exploring CUE's Go API
96 changes: 96 additions & 0 deletions content/docs/howto/validate-json-using-go-api/gen_cache.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package site
{
content: {
docs: {
howto: {
"validate-json-using-go-api": {
page: {
cache: {
upload: {
"cue schema": "ZpSJCm9G7J+MjuBIzc3pueaF87YCQpvv3hx5r8UwLLw="
"good data": "VxujqE1InR4iaC/qnP2xgIRqP1SFB8Khx9JCm/Z16Z8="
"bad data": "o7UoF2uK9CROfVxOPxkx6d58DoiGlhqxyWJsiLQw+pA="
"main go": "ZlWvRqx9OnyoDaf555iHnIXYgCPjz2igOpxpjyBF9Kc="
}
multi_step: {
hash: "7VTM00G63VNCUJDBFFR80NS40QASA7JSQLGC93V16BUTKK750VR0===="
scriptHash: "D7AL3UDDJL6UIBJ1L3ASO3RB5JHESRQ6II1N9R88SETKGSR84LI0===="
steps: [{
doc: ""
cmd: "export GOMODCACHE=/caches/gomodcache"
exitCode: 0
output: ""
}, {
doc: ""
cmd: "export GOCACHE=/caches/gobuild"
exitCode: 0
output: ""
}, {
doc: ""
cmd: "cue vet schema.cue good.json -d '#Schema'"
exitCode: 0
output: ""
}, {
doc: ""
cmd: "cue vet schema.cue bad.json -d '#Schema'"
exitCode: 1
output: """
age: conflicting values "173" and int (mismatched types string and int):
./bad.json:6:12
./schema.cue:3:9
name: conflicting values string and ["Moby","Dick"] (mismatched types string and list):
./bad.json:2:13
./schema.cue:2:9
"""
}, {
doc: "#ellipsis 0"
cmd: "go mod init go.example"
exitCode: 0
output: """
...
"""
}, {
doc: "#ellipsis 0"
cmd: "go get cuelang.org/[email protected]"
exitCode: 0
output: """
...
"""
}, {
doc: "#ellipsis 0"
cmd: "go mod tidy"
exitCode: 0
output: """
...
"""
}, {
doc: ""
cmd: "go run . good.json"
exitCode: 0
output: """
✅ JSON: ok
"""
}, {
doc: ""
cmd: "go run . bad.json"
exitCode: 1
output: """
❌ JSON: NOT ok
main.go:42: #Schema.name: conflicting values string and ["Moby","Dick"] (mismatched types string and list) (and 1 more errors)
exit status 1
"""
}]
}
}
}
}
}
}
}
}
3 changes: 3 additions & 0 deletions content/docs/howto/validate-json-using-go-api/page.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package site

content: docs: howto: "validate-json-using-go-api": page: _
Loading

0 comments on commit eac6463

Please sign in to comment.