diff --git a/pkg/tools/gen/genkcl_jsonschema.go b/pkg/tools/gen/genkcl_jsonschema.go index 7a41114e..0da475c4 100644 --- a/pkg/tools/gen/genkcl_jsonschema.go +++ b/pkg/tools/gen/genkcl_jsonschema.go @@ -385,36 +385,124 @@ func convertSchemaFromJsonSchema(ctx *convertContext, s *jsonschema.Schema, name } case *jsonschema.AllOf: schs := *v + var validations []*validation + _, req := required[name] for i := 0; i < len(schs); i++ { sch := schs[i] for _, key := range sch.OrderedKeywords { - if _, ok := s.Keywords[key]; !ok { - s.OrderedKeywords = append(s.OrderedKeywords, key) - s.Keywords[key] = sch.Keywords[key] - } else { - switch v := sch.Keywords[key].(type) { - case *jsonschema.Type: - case *jsonschema.Ref: - refSch := v.ResolveRef(ctx.rootSchema) - if refSch == nil || refSch.OrderedKeywords == nil { - logger.GetLogger().Warningf("failed to resolve ref: %s", v.Reference) - continue + switch v := sch.Keywords[key].(type) { + case *jsonschema.Minimum: + validations = append(validations, &validation{ + Name: name, + Required: req, + Minimum: (*float64)(v), + ExclusiveMinimum: false, + }) + case *jsonschema.Maximum: + validations = append(validations, &validation{ + Name: name, + Required: req, + Maximum: (*float64)(v), + ExclusiveMaximum: false, + }) + case *jsonschema.ExclusiveMinimum: + validations = append(validations, &validation{ + Name: name, + Required: req, + Minimum: (*float64)(v), + ExclusiveMinimum: true, + }) + case *jsonschema.ExclusiveMaximum: + validations = append(validations, &validation{ + Name: name, + Required: req, + Maximum: (*float64)(v), + ExclusiveMaximum: true, + }) + case *jsonschema.MinLength: + validations = append(validations, &validation{ + Name: name, + Required: req, + MinLength: (*int)(v), + }) + case *jsonschema.MaxLength: + validations = append(validations, &validation{ + Name: name, + Required: req, + MaxLength: (*int)(v), + }) + case *jsonschema.Pattern: + validations = append(validations, &validation{ + Name: name, + Required: req, + Regex: (*regexp.Regexp)(v), + }) + ctx.imports["regex"] = struct{}{} + case *jsonschema.MultipleOf: + vInt := int(*v) + if float64(vInt) != float64(*v) { + logger.GetLogger().Warningf("unsupported multipleOf value: %f", *v) + continue + } + result.Validations = append(result.Validations, validation{ + Name: name, + Required: req, + MultiplyOf: &vInt, + }) + case *jsonschema.UniqueItems: + if *v { + result.Validations = append(result.Validations, validation{ + Name: name, + Required: req, + Unique: true, + }) + } + case *jsonschema.MinItems: + result.Validations = append(result.Validations, validation{ + Name: name, + Required: req, + MinLength: (*int)(v), + }) + case *jsonschema.MaxItems: + result.Validations = append(result.Validations, validation{ + Name: name, + Required: req, + MaxLength: (*int)(v), + }) + default: + if _, ok := s.Keywords[key]; !ok { + s.OrderedKeywords = append(s.OrderedKeywords, key) + s.Keywords[key] = sch.Keywords[key] + } else { + switch v := sch.Keywords[key].(type) { + case *jsonschema.Type: + case *jsonschema.Ref: + refSch := v.ResolveRef(ctx.rootSchema) + if refSch == nil || refSch.OrderedKeywords == nil { + logger.GetLogger().Warningf("failed to resolve ref: %s", v.Reference) + continue + } + schs = append(schs, refSch) + case *jsonschema.Properties: + props := *s.Keywords[key].(*jsonschema.Properties) + props = append(props, *v...) + s.Keywords[key] = &props + case *jsonschema.Required: + reqs := *s.Keywords[key].(*jsonschema.Required) + reqs = append(reqs, *v...) + s.Keywords[key] = &reqs + default: + logger.GetLogger().Warningf("failed to merge allOf: unsupported keyword %s", key) } - schs = append(schs, refSch) - case *jsonschema.Properties: - props := *s.Keywords[key].(*jsonschema.Properties) - props = append(props, *v...) - s.Keywords[key] = &props - case *jsonschema.Required: - reqs := *s.Keywords[key].(*jsonschema.Required) - reqs = append(reqs, *v...) - s.Keywords[key] = &reqs - default: - logger.GetLogger().Warningf("failed to merge allOf: unsupported keyword %s", key) } } } } + if len(validations) > 0 { + result.Validations = append(result.Validations, validation{ + AllOf: validations, + }) + } sort.SliceStable(s.OrderedKeywords[i+1:], func(i, j int) bool { return jsonschema.GetKeywordOrder(s.OrderedKeywords[i]) < jsonschema.GetKeywordOrder(s.OrderedKeywords[j]) }) @@ -449,6 +537,13 @@ func convertSchemaFromJsonSchema(ctx *convertContext, s *jsonschema.Schema, name if result.HasIndexSignature && result.IndexSignature.validation != nil { result.Validations = append(result.Validations, *result.IndexSignature.validation) } + // Update AllOf validation required fields + for i := range result.Validations { + for j := range result.Validations[i].AllOf { + result.Validations[i].AllOf[j].Name = result.Validations[i].Name + result.Validations[i].AllOf[j].Required = result.Validations[i].Required + } + } result.property.Name = convertPropertyName(result.Name, ctx.castingOption) result.property.Description = result.Description return result diff --git a/pkg/tools/gen/templates/kcl/validator.gotmpl b/pkg/tools/gen/templates/kcl/validator.gotmpl index a9b86f83..85fea7c2 100644 --- a/pkg/tools/gen/templates/kcl/validator.gotmpl +++ b/pkg/tools/gen/templates/kcl/validator.gotmpl @@ -20,4 +20,7 @@ {{- if .Unique }} isunique({{ formatName .Name }}){{ if not .Required }} if {{ formatName .Name }}{{ end }} {{- end }} + {{- if .AllOf }} + {{- template "validator" .AllOf }} + {{- end }} {{- end -}} diff --git a/pkg/tools/gen/testdata/jsonschema/allof-validation/expect.k b/pkg/tools/gen/testdata/jsonschema/allof-validation/expect.k new file mode 100644 index 00000000..d7f11bae --- /dev/null +++ b/pkg/tools/gen/testdata/jsonschema/allof-validation/expect.k @@ -0,0 +1,25 @@ +""" +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" +import regex + +schema Config: + r""" + Schema for representing a config information. + + Attributes + ---------- + name : str, required + price : float, optional + """ + + name: str + price?: float + + check: + regex.match(name, r"198.160") + regex.match(name, r"198.161") + regex.match(name, r"198.162") + price >= 0 if price + diff --git a/pkg/tools/gen/testdata/jsonschema/allof-validation/input.json b/pkg/tools/gen/testdata/jsonschema/allof-validation/input.json new file mode 100644 index 00000000..ba1fd74b --- /dev/null +++ b/pkg/tools/gen/testdata/jsonschema/allof-validation/input.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/schemas/config", + "description": "Schema for representing a config information.", + "type": "object", + "properties": { + "name": { + "type": "string", + "allOf": [ + { + "pattern": "198.160" + }, + { + "pattern": "198.161" + }, + { + "pattern": "198.162" + } + ] + }, + "price": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "name" + ] +}