Skip to content

Enhancement: Support the JsonSchema "format" keyword for the KCL schema generation #440

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion pkg/tools/gen/genkcl_jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, "/"))
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/tools/gen/testdata/jsonschema/as3/expect.k
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down