Skip to content

Commit

Permalink
encoding/jsonschema: split Strict mode
Browse files Browse the repository at this point in the history
We'd like to be able to produce an error when an unimplemented feature
is used, but also avoid errors when unknown keywords are used (a common
practice).

This adds that capability to encoding/jsonschema. Initially, we will
default to the entirely non-strict mode, but in a subsequent CL, we'll
change to using strict features and non-strict keywords.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I4e8bf463548ec9a815a1215f6cf7afdf480bb703
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1200899
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
  • Loading branch information
rogpeppe committed Sep 10, 2024
1 parent cdf67fa commit 3831017
Show file tree
Hide file tree
Showing 24 changed files with 94 additions and 48 deletions.
2 changes: 1 addition & 1 deletion cmd/cue/cmd/testdata/script/def_jsonschema.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import "strings"
"foo": "bar"
}
-- expect-stderr --
unsupported constraint "foo":
unknown keyword "foo":
./bad.json:3:3
-- data.yaml --
age: twenty
Expand Down
6 changes: 5 additions & 1 deletion encoding/jsonschema/constraints_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ func constraintID(key string, n cue.Value, s *state) {
}

if u.Fragment != "" {
// TODO do not use Strict for this. The specification is clear:
// before 2019-09, IDs could contain plain-name fragments;
// (see https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.5)
// afterwards, $anchor was reserved for that purpose.
if s.cfg.Strict {
s.errf(n, "$id URI may not contain a fragment")
}
Expand Down Expand Up @@ -61,7 +65,7 @@ func constraintSchema(key string, n cue.Value, s *state) {
}

func constraintTODO(key string, n cue.Value, s *state) {
if s.cfg.Strict {
if s.cfg.StrictFeatures {
s.errf(n, `keyword %q not yet implemented`, key)
}
}
5 changes: 4 additions & 1 deletion encoding/jsonschema/constraints_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ func constraintMinLength(key string, n cue.Value, s *state) {
func constraintPattern(key string, n cue.Value, s *state) {
str, _ := n.String()
if _, err := regexp.Compile(str); err != nil {
if s.cfg.Strict {
if s.cfg.StrictFeatures {
// TODO check if the error is only because of an unsupported
// regexp feature (e.g. perl regexp) or because the regexp is just
// bad. If the latter, this should be an error even if Strict is false.
s.errf(n, "unsupported regexp: %v", err)
}
return
Expand Down
8 changes: 4 additions & 4 deletions encoding/jsonschema/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,18 +673,18 @@ func (s *state) schemaState(n cue.Value, types cue.Kind, idRef []label, isLogica
// Convert each constraint into a either a value or a functor.
c := constraintMap[key]
if c == nil {
if pass == 0 && s.cfg.Strict {
if pass == 0 && s.cfg.StrictKeywords {
// TODO: value is not the correct position, albeit close. Fix this.
s.warnf(value.Pos(), "unsupported constraint %q", key)
s.warnf(value.Pos(), "unknown keyword %q", key)
}
return
}
if c.phase != pass {
return
}
if !c.versions.contains(state.schemaVersion) {
if s.cfg.Strict {
s.warnf(value.Pos(), "constraint %q is not supported in JSON schema version %v", key, state.schemaVersion)
if s.cfg.StrictKeywords {
s.warnf(value.Pos(), "keyword %q is not supported in JSON schema version %v", key, state.schemaVersion)
}
return
}
Expand Down
2 changes: 2 additions & 0 deletions encoding/jsonschema/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ func TestDecode(t *testing.T) {
}
}
cfg.Strict = t.HasTag("strict")
cfg.StrictKeywords = t.HasTag("strictKeywords")
cfg.StrictFeatures = t.HasTag("strictFeatures")

ctx := t.CueContext()

Expand Down
17 changes: 15 additions & 2 deletions encoding/jsonschema/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func Extract(data cue.InstanceOrValue, cfg *Config) (f *ast.File, err error) {
if cfg.DefaultVersion == VersionUnknown {
cfg.DefaultVersion = VersionDraft7
}
if cfg.Strict {
cfg.StrictKeywords = true
cfg.StrictFeatures = true
}
d := &decoder{
cfg: cfg,
mapURLErrors: make(map[string]bool),
Expand Down Expand Up @@ -102,10 +106,19 @@ type Config struct {
// - selection and definition of formats
// - documentation hooks.

// Strict reports an error for unsupported features, rather than ignoring
// them.
// Strict reports an error for unsupported features and keywords,
// rather than ignoring them. When true, this is equivalent to
// setting both StrictFeatures and StrictKeywords to true.
Strict bool

// StrictFeatures reports an error for features that are known
// to be unsupported.
StrictFeatures bool

// StrictKeywords reports an error when unknown keywords
// are encountered.
StrictKeywords bool

// DefaultVersion holds the default schema version to use
// when no $schema field is present. If it is zero, [DefaultVersion]
// will be used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@
"valid": true,
"skip": {
"v2": "incompatible list lengths (2 and 3)\n0: incompatible list lengths (1 and 2)\n1: incompatible list lengths (1 and 2)\n",
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n"
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n0: incompatible list lengths (1 and 2):\n generated.cue:4:8\n"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
}
},
"skip": {
"v2": "extract error: unsupported constraint \"unknown-keyword\"",
"v3": "extract error: unsupported constraint \"unknown-keyword\""
"v2": "extract error: unknown keyword \"unknown-keyword\"",
"v3": "extract error: unknown keyword \"unknown-keyword\""
},
"tests": [
{
Expand Down Expand Up @@ -57,8 +57,8 @@
}
},
"skip": {
"v2": "extract error: unsupported constraint \"unknown-keyword\"",
"v3": "extract error: unsupported constraint \"unknown-keyword\""
"v2": "extract error: unknown keyword \"unknown-keyword\"",
"v3": "extract error: unknown keyword \"unknown-keyword\""
},
"tests": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
]
},
"skip": {
"v2": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)"
"v2": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)"
},
"tests": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
}
},
"skip": {
"v2": "extract error: unsupported constraint \"unknown-keyword\"",
"v3": "extract error: unsupported constraint \"unknown-keyword\""
"v2": "extract error: unknown keyword \"unknown-keyword\"",
"v3": "extract error: unknown keyword \"unknown-keyword\""
},
"tests": [
{
Expand Down Expand Up @@ -57,8 +57,8 @@
}
},
"skip": {
"v2": "extract error: unsupported constraint \"unknown-keyword\"",
"v3": "extract error: unsupported constraint \"unknown-keyword\""
"v2": "extract error: unknown keyword \"unknown-keyword\"",
"v3": "extract error: unknown keyword \"unknown-keyword\""
},
"tests": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
]
},
"skip": {
"v2": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)"
"v2": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)"
},
"tests": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@
"valid": true,
"skip": {
"v2": "incompatible list lengths (2 and 3)\n0: incompatible list lengths (1 and 2)\n1: incompatible list lengths (1 and 2)\n",
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n"
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n0: incompatible list lengths (1 and 2):\n generated.cue:4:8\n"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
]
},
"skip": {
"v2": "extract error: constraint \"const\" is not supported in JSON schema version http://json-schema.org/draft-04/schema#",
"v3": "extract error: constraint \"const\" is not supported in JSON schema version http://json-schema.org/draft-04/schema#"
"v2": "extract error: keyword \"const\" is not supported in JSON schema version http://json-schema.org/draft-04/schema#",
"v3": "extract error: keyword \"const\" is not supported in JSON schema version http://json-schema.org/draft-04/schema#"
},
"tests": [
{
Expand Down
4 changes: 2 additions & 2 deletions encoding/jsonschema/testdata/external/tests/draft4/ref.json
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@
]
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-04/schema# (and 1 more errors)",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-04/schema# (and 1 more errors)"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-04/schema# (and 1 more errors)",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-04/schema# (and 1 more errors)"
},
"tests": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@
"valid": true,
"skip": {
"v2": "incompatible list lengths (2 and 3)\n0: incompatible list lengths (1 and 2)\n1: incompatible list lengths (1 and 2)\n",
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n"
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n0: incompatible list lengths (1 and 2):\n generated.cue:4:8\n"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
]
},
"skip": {
"v2": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)"
"v2": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)"
},
"tests": [
{
Expand Down
24 changes: 12 additions & 12 deletions encoding/jsonschema/testdata/external/tests/draft6/ref.json
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@
]
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema# (and 1 more errors)",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema# (and 1 more errors)"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema# (and 1 more errors)",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema# (and 1 more errors)"
},
"tests": [
{
Expand Down Expand Up @@ -994,8 +994,8 @@
}
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
},
"tests": [
{
Expand Down Expand Up @@ -1039,8 +1039,8 @@
}
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
},
"tests": [
{
Expand Down Expand Up @@ -1084,8 +1084,8 @@
}
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
},
"tests": [
{
Expand Down Expand Up @@ -1129,8 +1129,8 @@
}
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
},
"tests": [
{
Expand Down Expand Up @@ -1174,8 +1174,8 @@
}
},
"skip": {
"v2": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: constraint \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
"v2": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#",
"v3": "extract error: keyword \"$comment\" is not supported in JSON schema version http://json-schema.org/draft-06/schema#"
},
"tests": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@
"valid": true,
"skip": {
"v2": "incompatible list lengths (2 and 3)\n0: incompatible list lengths (1 and 2)\n1: incompatible list lengths (1 and 2)\n",
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n"
"v3": "incompatible list lengths (2 and 3):\n generated.cue:2:1\n0: incompatible list lengths (1 and 2):\n generated.cue:4:8\n"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
]
},
"skip": {
"v2": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unsupported constraint \"array_of_schemas\" (and 1 more errors)"
"v2": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)",
"v3": "extract error: unknown keyword \"array_of_schemas\" (and 1 more errors)"
},
"tests": [
{
Expand Down
2 changes: 1 addition & 1 deletion encoding/jsonschema/testdata/txtar/defaultversion.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
}
-- out/decode/extract --
ERROR:
constraint "$id" is not supported in JSON schema version http://json-schema.org/draft-04/schema#:
keyword "$id" is not supported in JSON schema version http://json-schema.org/draft-04/schema#:
schema.json:2:3
2 changes: 1 addition & 1 deletion encoding/jsonschema/testdata/txtar/newid_oldversion.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
}
-- out/decode/extract --
ERROR:
constraint "$id" is not supported in JSON schema version http://json-schema.org/draft-04/schema#:
keyword "$id" is not supported in JSON schema version http://json-schema.org/draft-04/schema#:
schema.json:3:3
2 changes: 1 addition & 1 deletion encoding/jsonschema/testdata/txtar/oldid_newversion.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
}
-- out/decode/extract --
ERROR:
constraint "id" is not supported in JSON schema version http://json-schema.org/draft-07/schema#:
keyword "id" is not supported in JSON schema version http://json-schema.org/draft-07/schema#:
schema.json:3:3
12 changes: 12 additions & 0 deletions encoding/jsonschema/testdata/txtar/strictfeatures.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#strictFeatures
-- schema.json --
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "number",
"$dynamicAnchor": "bar",
"foo": true
}
-- out/decode/extract --
ERROR:
keyword "$dynamicAnchor" not yet implemented:
schema.json:4:3
12 changes: 12 additions & 0 deletions encoding/jsonschema/testdata/txtar/strictkeywords.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#strictKeywords
-- schema.json --
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "number",
"$dynamicAnchor": "bar",
"foo": true
}
-- out/decode/extract --
ERROR:
unknown keyword "foo":
schema.json:5:3

0 comments on commit 3831017

Please sign in to comment.