Skip to content
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

feat: enhance generation of kcl schema from json schema #135

Merged
merged 4 commits into from
Aug 14, 2023
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
57 changes: 41 additions & 16 deletions pkg/tools/gen/genkcl_jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,18 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name
return convertResult{IsSchema: false}
}

result := convertResult{IsSchema: false, Name: name}
if result.Name == "" {
result.Name = "MyType"
// for the name of the result, we prefer $id, then title, then name in parameter
// if none of them exists, "MyType" as default
if id, ok := s.Keywords["$id"].(*jsonschema.ID); ok {
lastSlashIndex := strings.LastIndex(string(*id), "/")
name = strings.Replace(string(*id)[lastSlashIndex+1:], ".json", "", -1)
} else if title, ok := s.Keywords["title"].(*jsonschema.Title); ok {
name = string(*title)
}
if name == "" {
name = "MyType"
}
result := convertResult{IsSchema: false, Name: name}

isArray := false
typeList := typeUnion{}
Expand All @@ -86,11 +94,6 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name
case *jsonschema.Comment:
case *jsonschema.SchemaURI:
case *jsonschema.ID:
// if the schema has ID, use it as the name
lastSlashIndex := strings.LastIndex(string(*v), "/")
if lastSlashIndex != -1 {
result.Name = strings.Trim(string(*v)[lastSlashIndex+1:], ".json")
}
case *jsonschema.Description:
result.Description = string(*v)
case *jsonschema.Type:
Expand Down Expand Up @@ -145,6 +148,7 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name
result.HasDefault = true
result.DefaultValue = v.Data
case *jsonschema.Enum:
typeList.Items = make([]typeInterface, 0, len(*v))
for _, val := range *v {
unmarshalledVal := interface{}(nil)
err := json.Unmarshal(val, &unmarshalledVal)
Expand All @@ -163,17 +167,30 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name
logger.GetLogger().Warningf("failed to unmarshal const value: %s", err)
continue
}
typeList.Items = append(typeList.Items, typeValue{
Value: unmarshalledVal,
})
typeList.Items = []typeInterface{typeValue{Value: unmarshalledVal}}
result.HasDefault = true
result.DefaultValue = unmarshalledVal
case *jsonschema.Ref:
typeName := strcase.ToCamel(v.Reference[strings.LastIndex(v.Reference, "/")+1:])
typeList.Items = append(typeList.Items, typeCustom{Name: typeName})
typeList.Items = []typeInterface{typeCustom{Name: typeName}}
case *jsonschema.Defs:
for key, val := range *v {
ctx.resultMap[key] = convertSchemaFromJsonSchema(ctx, val, key)
sch := convertSchemaFromJsonSchema(ctx, val, key)
if !sch.IsSchema {
logger.GetLogger().Warningf("unsupported defining non-object: %s", key)
sch = convertResult{
IsSchema: true,
Name: key,
schema: schema{
Name: strcase.ToCamel(key),
HasIndexSignature: true,
IndexSignature: indexSignature{
Type: typePrimitive(typAny),
},
},
}
}
ctx.resultMap[key] = sch
}
case *jsonschema.AdditionalProperties:
switch v.SchemaType {
Expand Down Expand Up @@ -254,11 +271,19 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name

if result.IsSchema {
result.Type = typeCustom{Name: strcase.ToCamel(name)}
if len(result.Properties) == 0 && !result.HasIndexSignature {
result.HasIndexSignature = true
result.IndexSignature = indexSignature{Type: typePrimitive(typAny)}
}
} else {
if isArray {
result.Type = typeArray{Items: typeList}
if len(typeList.Items) != 0 {
if isArray {
result.Type = typeArray{Items: typeList}
} else {
result.Type = typeList
}
} else {
result.Type = typeList
result.Type = typePrimitive(typAny)
}
}
result.schema.Name = strcase.ToCamel(result.Name)
Expand Down
32 changes: 32 additions & 0 deletions pkg/tools/gen/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,39 @@ func formatValue(v interface{}) string {
}
}

var kclKeywords = map[string]struct{}{
"True": {},
"False": {},
"None": {},
"Undefined": {},
"import": {},
"and": {},
"or": {},
"in": {},
"is": {},
"not": {},
"as": {},
"if": {},
"else": {},
"elif": {},
"for": {},
"schema": {},
"mixin": {},
"protocol": {},
"check": {},
"assert": {},
"all": {},
"any": {},
"map": {},
"filter": {},
"lambda": {},
"rule": {},
}

func formatName(name string) string {
if _, ok := kclKeywords[name]; ok {
return fmt.Sprintf("$%s", name)
}
return name
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/tools/gen/testdata/jsonschema/basic/expect.k
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ schema Book:
price : float, optional
available : bool, optional, default is True
category : "Fiction" | "Science" | "History", optional
$rule : str, optional
"""

title: str
authors?: [str]
price?: float
available?: bool = True
category?: "Fiction" | "Science" | "History"
$rule?: str

4 changes: 4 additions & 0 deletions pkg/tools/gen/testdata/jsonschema/basic/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
"default": true
},
"category": {
"type": "string",
"enum": [
"Fiction",
"Science",
"History"
]
},
"rule": {
"type": "string"
}
},
"required": [
Expand Down
39 changes: 39 additions & 0 deletions pkg/tools/gen/testdata/jsonschema/unsupport/expect.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
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.
"""

schema Shop:
"""
Schema for representing a shop information.
In this test case, we use some logic keywords like "oneOf" that can't be directly converted at the moment. To make it still work, we'll convert it into "any" type.

Attributes
----------
products : any, optional
"""

products?: any

schema Clothing:
"""
Clothing
"""

[...str]: any

schema Product:
"""
Product

Attributes
----------
name : str, optional
price : float, optional
"""

name?: str
price?: float

check:
price >= 0
56 changes: 56 additions & 0 deletions pkg/tools/gen/testdata/jsonschema/unsupport/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/schemas/shop",
"description": "Schema for representing a shop information.\nIn this test case, we use some logic keywords like \"oneOf\" that can't be directly converted at the moment. To make it still work, we'll convert it into \"any\" type.",
"$defs": {
"product": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"price": {
"type": "number",
"minimum": 0
}
}
},
"clothing": {
"allOf": [
{
"$ref": "#/$defs/product"
},
{
"type": "object",
"properties": {
"type": {
"const": "clothing"
},
"material": {
"type": "string"
}
}
}
]
}
},
"type": "object",
"properties": {
"products": {
"oneOf": [
{
"$ref": "#/$defs/clothing"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/clothing"
}
},
{
"const": "empty"
}
]
}
}
}
Loading