From dfe255f77256907390ca3dde2b129a35011b0d46 Mon Sep 17 00:00:00 2001 From: Junxing Zhu Date: Tue, 12 Dec 2023 17:34:39 +0800 Subject: [PATCH] fix: generate schema for nested object in array (#197) Signed-off-by: jakezhu9 --- pkg/tools/gen/genkcl_jsonschema.go | 35 +++++--- .../testdata/jsonschema/additional/expect.k | 8 +- .../testdata/jsonschema/nested-items/expect.k | 86 +++++++++++++++++++ .../jsonschema/nested-items/input.json | 81 +++++++++++++++++ .../gen/testdata/jsonschema/nested/expect.k | 8 +- 5 files changed, 198 insertions(+), 20 deletions(-) create mode 100644 pkg/tools/gen/testdata/jsonschema/nested-items/expect.k create mode 100644 pkg/tools/gen/testdata/jsonschema/nested-items/input.json diff --git a/pkg/tools/gen/genkcl_jsonschema.go b/pkg/tools/gen/genkcl_jsonschema.go index be8e0e10..dda95cbc 100644 --- a/pkg/tools/gen/genkcl_jsonschema.go +++ b/pkg/tools/gen/genkcl_jsonschema.go @@ -6,6 +6,7 @@ import ( "io" "path/filepath" "regexp" + "strconv" "strings" "github.com/iancoleman/strcase" @@ -16,6 +17,7 @@ import ( type convertContext struct { imports map[string]struct{} resultMap map[string]convertResult + paths []string } type convertResult struct { @@ -42,8 +44,9 @@ func (k *kclGenerator) genSchemaFromJsonSchema(w io.Writer, filename string, src ctx := convertContext{ resultMap: make(map[string]convertResult), imports: make(map[string]struct{}), + paths: []string{}, } - result := convertSchemaFromJsonSchema(ctx, js, + result := convertSchemaFromJsonSchema(&ctx, js, strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename))) if !result.IsSchema { panic("result is not schema") @@ -65,7 +68,7 @@ func (k *kclGenerator) genSchemaFromJsonSchema(w io.Writer, filename string, src return k.genKcl(w, kclSch) } -func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name string) convertResult { +func convertSchemaFromJsonSchema(ctx *convertContext, s *jsonschema.Schema, name string) convertResult { // in jsonschema, type is one of True, False and Object // we only convert Object type if s.SchemaType != jsonschema.SchemaTypeObject { @@ -84,6 +87,7 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name name = "MyType" } result := convertResult{IsSchema: false, Name: name} + ctx.paths = append(ctx.paths, name) isArray := false typeList := typeUnion{} @@ -113,10 +117,11 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name logger.GetLogger().Warningf("unsupported multiple items: %#v", v) break } - for _, i := range v.Schemas { - item := convertSchemaFromJsonSchema(ctx, i, "items") + for i, val := range v.Schemas { + item := convertSchemaFromJsonSchema(ctx, val, "items"+strconv.Itoa(i)) if item.IsSchema { - typeList.Items = append(typeList.Items, typeCustom{Name: item.Name}) + ctx.resultMap[item.schema.Name] = item + typeList.Items = append(typeList.Items, typeCustom{Name: item.schema.Name}) } else { typeList.Items = append(typeList.Items, item.Type) } @@ -132,14 +137,12 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name propSch := convertSchemaFromJsonSchema(ctx, val, key) _, propSch.Required = required[key] if propSch.IsSchema { - propSch.Name = strcase.ToCamel(key) - ctx.resultMap[propSch.Name] = propSch + ctx.resultMap[propSch.schema.Name] = propSch } - propSch.Name = strcase.ToSnake(key) result.Properties = append(result.Properties, propSch.property) if !propSch.IsSchema { for _, validate := range propSch.Validations { - validate.Name = propSch.Name + validate.Name = propSch.property.Name result.Validations = append(result.Validations, validate) } } @@ -174,6 +177,8 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name typeName := strcase.ToCamel(v.Reference[strings.LastIndex(v.Reference, "/")+1:]) typeList.Items = []typeInterface{typeCustom{Name: typeName}} case *jsonschema.Defs: + paths := ctx.paths + ctx.paths = []string{} for key, val := range *v { sch := convertSchemaFromJsonSchema(ctx, val, key) if !sch.IsSchema { @@ -192,6 +197,7 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name } ctx.resultMap[key] = sch } + ctx.paths = paths case *jsonschema.AdditionalProperties: switch v.SchemaType { case jsonschema.SchemaTypeObject: @@ -270,7 +276,13 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name } if result.IsSchema { - result.Type = typeCustom{Name: strcase.ToCamel(name)} + var s strings.Builder + for _, p := range ctx.paths { + s.WriteString(strcase.ToCamel(p)) + } + result.schema.Name = s.String() + result.schema.Description = result.Description + result.Type = typeCustom{Name: strcase.ToCamel(result.schema.Name)} if len(result.Properties) == 0 && !result.HasIndexSignature { result.HasIndexSignature = true result.IndexSignature = indexSignature{Type: typePrimitive(typAny)} @@ -286,10 +298,9 @@ func convertSchemaFromJsonSchema(ctx convertContext, s *jsonschema.Schema, name result.Type = typePrimitive(typAny) } } - result.schema.Name = strcase.ToCamel(result.Name) - result.schema.Description = result.Description result.property.Name = strcase.ToSnake(result.Name) result.property.Description = result.Description + ctx.paths = ctx.paths[:len(ctx.paths)-1] return result } diff --git a/pkg/tools/gen/testdata/jsonschema/additional/expect.k b/pkg/tools/gen/testdata/jsonschema/additional/expect.k index 466b6dd8..22939902 100644 --- a/pkg/tools/gen/testdata/jsonschema/additional/expect.k +++ b/pkg/tools/gen/testdata/jsonschema/additional/expect.k @@ -10,15 +10,15 @@ schema Ethernet: Attributes ---------- name : str, optional - socket : Socket, optional, default is {"HTTP": 80, "HTTPS": 443} + socket : EthernetSocket, optional, default is {"HTTP": 80, "HTTPS": 443} """ name?: str - socket?: Socket = {"HTTP": 80, "HTTPS": 443} + socket?: EthernetSocket = {"HTTP": 80, "HTTPS": 443} -schema Socket: +schema EthernetSocket: """ - Socket + EthernetSocket """ [...str]: int diff --git a/pkg/tools/gen/testdata/jsonschema/nested-items/expect.k b/pkg/tools/gen/testdata/jsonschema/nested-items/expect.k new file mode 100644 index 00000000..b1a4a2ff --- /dev/null +++ b/pkg/tools/gen/testdata/jsonschema/nested-items/expect.k @@ -0,0 +1,86 @@ +""" +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 MonacoManifestSchema: + """ + MonacoManifestSchema + + Attributes + ---------- + manifest_version : str, required + The schema version this manifest conforms to - e.g. 1.0 + projects : [MonacoManifestSchemaProjectsItems0], required + The projects grouped by this manifest + environment_groups : [MonacoManifestSchemaEnvironmentGroupsItems0], required + The Environment groups to which projects in this manifest are deployed + """ + + manifest_version: str + projects: [MonacoManifestSchemaProjectsItems0] + environment_groups: [MonacoManifestSchemaEnvironmentGroupsItems0] + +schema MonacoManifestSchemaEnvironmentGroupsItems0: + """ + MonacoManifestSchemaEnvironmentGroupsItems0 + + Attributes + ---------- + name : str, optional + The name of this environment group + environments : [MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0], optional + The environments in this group + """ + + name?: str + environments?: [MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0] + +schema MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0: + """ + MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0 + + Attributes + ---------- + name : str, optional + The name of this environment + url : MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0Url, optional + The URL of this environment + """ + + name?: str + url?: MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0Url + +schema MonacoManifestSchemaEnvironmentGroupsItems0EnvironmentsItems0Url: + """ + The URL of this environment + + Attributes + ---------- + type : str, optional + Optional Type of URL definition. + value : str, optional + The value of the URL, based on type either an URL or environment variable name + """ + + type?: str + value?: str + +schema MonacoManifestSchemaProjectsItems0: + """ + MonacoManifestSchemaProjectsItems0 + + Attributes + ---------- + name : str, required + The name of this project + type : str, optional + Optional Type of this project. Default: Simple + path : str, optional + Optional filepath of the project relative to the manifest.yaml location. Defaults to name + """ + + name: str + type?: str + path?: str + diff --git a/pkg/tools/gen/testdata/jsonschema/nested-items/input.json b/pkg/tools/gen/testdata/jsonschema/nested-items/input.json new file mode 100644 index 00000000..db880417 --- /dev/null +++ b/pkg/tools/gen/testdata/jsonschema/nested-items/input.json @@ -0,0 +1,81 @@ +{ + "$id": "https://example.com/monaco.manifest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Dynatrace Monitoring as Code Manifest File", + "type": "object", + "properties": { + "manifestVersion": { + "type": "string", + "description": "The schema version this manifest conforms to - e.g. 1.0" + }, + "projects": { + "type": "array", + "description": "The projects grouped by this manifest", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of this project" + }, + "type": { + "type": "string", + "description": "Optional Type of this project. Default: Simple" + }, + "path": { + "type": "string", + "description": "Optional filepath of the project relative to the manifest.yaml location. Defaults to name" + } + }, + "required": [ + "name" + ] + } + }, + "environmentGroups": { + "description": "The Environment groups to which projects in this manifest are deployed", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of this environment group" + }, + "environments": { + "description": "The environments in this group", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of this environment" + }, + "url": { + "description": "The URL of this environment", + "type": "object", + "properties": { + "type": { + "description": "Optional Type of URL definition.", + "type": "string" + }, + "value": { + "type": "string", + "description": "The value of the URL, based on type either an URL or environment variable name" + } + } + } + } + } + } + } + } + } + }, + "required": [ + "manifestVersion", + "projects", + "environmentGroups" + ] +} \ No newline at end of file diff --git a/pkg/tools/gen/testdata/jsonschema/nested/expect.k b/pkg/tools/gen/testdata/jsonschema/nested/expect.k index fdb66181..b64ad53d 100644 --- a/pkg/tools/gen/testdata/jsonschema/nested/expect.k +++ b/pkg/tools/gen/testdata/jsonschema/nested/expect.k @@ -10,15 +10,15 @@ schema Book: Attributes ---------- title : str, optional - author : Author, optional + author : BookAuthor, optional """ title?: str - author?: Author + author?: BookAuthor -schema Author: +schema BookAuthor: """ - Author + BookAuthor Attributes ----------