diff --git a/pkg/tools/gen/genkcl_jsonschema.go b/pkg/tools/gen/genkcl_jsonschema.go index bf9052b2..bc893cd4 100644 --- a/pkg/tools/gen/genkcl_jsonschema.go +++ b/pkg/tools/gen/genkcl_jsonschema.go @@ -619,7 +619,46 @@ func convertSchemaFromJsonSchema(ctx *convertContext, s *jsonschema.Schema, name // Do nothing for the readOnly keyword. logger.GetLogger().Infof("unsupported keyword: %s, path: %s, omit it", k, strings.Join(ctx.paths, "/")) case *jsonschema.Format: - logger.GetLogger().Warningf("unsupported keyword: %s, path: %s, see the issue: https://github.com/kcl-lang/kcl-go/issues/375", k, strings.Join(ctx.paths, "/")) + format := string(*v) + // Determine validation name and required status + var validationName string + var required bool + if len(ctx.paths) >= 2 { + validationName = ctx.paths[len(ctx.paths)-1] + required = result.property.Required + } else { + validationName = result.Name + required = true + } + var regexPattern *regexp.Regexp + switch format { + case "date-time": + regexPattern = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$`) + case "email": + regexPattern = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) + case "hostname": + regexPattern = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$`) + case "ipv4": + regexPattern = regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`) + case "ipv6": + regexPattern = regexp.MustCompile(`^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))$`) + case "uri": + regexPattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9+-.]*://[^/?#]+(?:/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$`) + case "uuid": + regexPattern = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) + default: + logger.GetLogger().Warningf("unsupported format: %s, path: %s", format, strings.Join(ctx.paths, "/")) + regexPattern = nil + } + if regexPattern != nil { + result.Validations = append(result.Validations, validation{ + Name: validationName, + Required: required, + Regex: regexPattern, + }) + result.Type = typePrimitive(typStr) + ctx.imports["regex"] = struct{}{} // Ensure regex import is included in KCL + } default: logger.GetLogger().Warningf("unsupported keyword: %s, path: %s", k, strings.Join(ctx.paths, "/")) } diff --git a/pkg/tools/gen/testdata/jsonschema/as3/expect.k b/pkg/tools/gen/testdata/jsonschema/as3/expect.k index 697a6fdd..52d4a95c 100644 --- a/pkg/tools/gen/testdata/jsonschema/as3/expect.k +++ b/pkg/tools/gen/testdata/jsonschema/as3/expect.k @@ -51,6 +51,7 @@ schema Adc: [...str]: Tenant check: + regex.match("$schema", r"^[a-zA-Z][a-zA-Z0-9+-.]*://[^/?#]+(?:/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$") if "$schema" len(id) <= 255 if id len(id) >= 0 if id regex.match(id, r"^[^\x00-\x20\x22'<>\x5c^`|\x7f]*$") if id @@ -213,6 +214,7 @@ schema As3: patchBody?: [As3PatchItem | As3PatchItem] check: + regex.match("$schema", r"^[a-zA-Z][a-zA-Z0-9+-.]*://[^/?#]+(?:/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$") if "$schema" redeployAge <= 15 if redeployAge redeployAge >= 0 if redeployAge historyLimit <= 15 if historyLimit @@ -337,6 +339,9 @@ schema As3OneOf0DeclarationConstants: timestamp?: str [...str]: bool | int | float | str | [any] | any + check: + regex.match(timestamp, r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$") if timestamp + schema As3OneOf0DeclarationTarget: r""" Trusted target for config when configuring with BIG-IQ @@ -486,6 +491,9 @@ schema As3OneOf1Constants: timestamp?: str [...str]: bool | int | float | str | [any] | any + check: + regex.match(timestamp, r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$") if timestamp + schema As3OneOf1Target: r""" Trusted target for config when configuring with BIG-IQ @@ -625,6 +633,9 @@ schema As3OneOf3Items0DeclarationConstants: timestamp?: str [...str]: bool | int | float | str | [any] | any + check: + regex.match(timestamp, r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$") if timestamp + schema As3OneOf3Items0DeclarationTarget: r""" Trusted target for config when configuring with BIG-IQ @@ -789,6 +800,9 @@ schema As3OneOf4Items0Constants: timestamp?: str [...str]: bool | int | float | str | [any] | any + check: + regex.match(timestamp, r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$") if timestamp + schema As3OneOf4Items0Target: r""" Trusted target for config when configuring with BIG-IQ @@ -872,6 +886,9 @@ schema Controls: traceResponse?: bool = False userAgent?: str + check: + regex.match(archiveTimestamp, r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$") if archiveTimestamp + schema Tenant: r""" Declares a Tenant