cue icon indicating copy to clipboard operation
cue copied to clipboard

cmd/cue: cannot use a reference via -l input jsonschema

Open joeriddles opened this issue 11 months ago • 1 comments

What version of CUE are you using (cue version)?

$ cue version
cue version v0.12.0

go version go1.23.4
      -buildmode exe
       -compiler gc
  DefaultGODEBUG asynctimerchan=1,gotypesalias=0,httpservecontentkeepheaders=1,tls3des=1,tlskyber=0,x509keypairleaf=0,x509negativeserial=1
     CGO_ENABLED 1
          GOARCH arm64
            GOOS darwin
         GOARM64 v8.0
cue.lang.version v0.12.0

Does this issue reproduce with the latest stable release?

Yes

What did you do?

❯ echo '{"$schema": "http://json-schema.org/draft-07/schema", "$id": "https://example.com/person.schema.json", "title": "Person","type": "object"}' | cue import -l title  jsonschema: -
error evaluating label title: reference "title" not found

JSON schema:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "$id": "https://example.com/person.schema.json",
  "title": "Person",
  "type": "object"
}

What did you expect to see?

The same output as using json and no error:

❯ echo '{"$schema": "http://json-schema.org/draft-07/schema", "$id": "https://example.com/person.schema.json", "title": "Person","type": "object"}' | cue import -l title  json: -      
Person: {
        $schema: "http://json-schema.org/draft-07/schema"
        $id:     "https://example.com/person.schema.json"
        title:   "Person"
        type:    "object"
}

JSON:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "$id": "https://example.com/person.schema.json",
  "title": "Person",
  "type": "object"
}

What did you see instead?

An error, error evaluating label title: reference "title" not found

joeriddles avatar Feb 03 '25 21:02 joeriddles

Hi @joeriddles and welcome to CUE!

Reworking your example slightly as a standalone reproduction:

exec cue import -l title jsonschema: input
cmp input.cue input.cue.golden

-- input --
{"$schema": "http://json-schema.org/draft-07/schema", "$id": "https://example.com/person.schema.json", "title": "Person","type": "object"}
-- input.cue.golden --
#Person: {
	$schema: "http://json-schema.org/draft-07/schema"
	$id:     "https://example.com/person.schema.json"
	title:   "Person"
	type:    "object"
}

The expectation is that this should succeed, but it fails with:

> exec cue import -l title jsonschema: input
[stderr]
error evaluating label title: reference "title" not found
[exit status 1]
FAIL: /tmp/testscript2318047493/repro.txtar/script.txtar:1: unexpected command failure

FWIW you can use the Go API to do this:

go mod tidy
go run . input
cmp input.cue input.cue.golden

-- go.mod --
module mod.example

go 1.23.5

require cuelang.org/go v0.12.0
-- main.go --
package main

import (
	"flag"
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/cuecontext"
	"cuelang.org/go/cue/format"
	"cuelang.org/go/cue/token"
	"cuelang.org/go/encoding/json"
	"cuelang.org/go/encoding/jsonschema"
)

func main() {
	// Iterate over the files listed as arguments, and extract a JSON Schema
	// from each, using the 'title' field as the name of the definition.

	flag.Parse()

	ctx := cuecontext.New()

	var exitCode int
	for _, f := range flag.Args() {
		if err := importJSONSchemaWithTitle(ctx, f); err != nil {
			exitCode++
			fmt.Fprintf(os.Stderr, "%v: %v\n", f, err)
		}
	}
	os.Exit(exitCode)
}

func importJSONSchemaWithTitle(ctx *cue.Context, inputFile string) error {
	byts, err := os.ReadFile(inputFile)
	if err != nil {
		return fmt.Errorf("failed to read %s: %v", inputFile, err)
	}
	expr, err := json.Extract(inputFile, byts)
	if err != nil {
		return fmt.Errorf("failed to extract JSON from %s: %v", inputFile, err)
	}
	v := ctx.BuildExpr(expr)
	if err := v.Err(); err != nil {
		return fmt.Errorf("failed to build CUE value: %v", err)
	}
	title, err := v.LookupPath(cue.ParsePath("title")).String()
	if err != nil {
		return fmt.Errorf("failed to lookup title from CUE value: %v", err)
	}
	schemaFile, err := jsonschema.Extract(v, &jsonschema.Config{
		Strict: true,
	})
	if err != nil {
		return fmt.Errorf("failed to extract JSON Schema from CUE value: %v", err)
	}
	schema := ctx.BuildFile(schemaFile)
	if err := schema.Err(); err != nil {
		return fmt.Errorf("schema was in error: %v", err)
	}
	def := ctx.CompileString("{}")
	def = def.FillPath(cue.MakePath(cue.Def(title)), schema)

	defFile := toFile(def.Syntax())

	defByts, err := format.Node(defFile)
	if err != nil {
		return fmt.Errorf("failed to convert definition to syntax: %v", err)
	}
	targetFileName := inputFile + ".cue"
	if err := os.WriteFile(targetFileName, defByts, 0666); err != nil {
		return fmt.Errorf("failed to write %s: %v", targetFileName, err)
	}
	return nil
}

// toFile is a helper that we should make available in one of our APIs to
// simplify the writing of a CUE value to a file, eliding unnecessary {}'s
func toFile(n ast.Node) *ast.File {
	if n == nil {
		return nil
	}
	switch n := n.(type) {
	case *ast.StructLit:
		f := &ast.File{Decls: n.Elts}
		ast.SetComments(f, ast.Comments(n))
		return f
	case ast.Expr:
		ast.SetRelPos(n, token.NoSpace)
		return &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: n}}}
	case *ast.File:
		return n
	default:
		panic(fmt.Sprintf("Unsupported node type %T", n))
	}
}
-- input --
{"$schema": "http://json-schema.org/draft-07/schema", "$id": "https://example.com/person.schema.json", "title": "Person","type": "object"}
-- input.cue.golden --
#Person: {
	@jsonschema(schema="http://json-schema.org/draft-07/schema#")
	@jsonschema(id="https://example.com/person.schema.json")
	...
}

myitcv avatar Feb 04 '25 09:02 myitcv