From fa1f4ad2889e10570a734f8b3fd4a68407d0b563 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Mon, 17 Feb 2025 07:32:57 -0800 Subject: [PATCH] Fix clashing class names when generating Pkl from OpenAPI/JSON Schema (#98) --- packages/k8s.contrib.crd/PklProject | 4 +- packages/k8s.contrib.crd/PklProject.deps.json | 2 +- .../internal/ModuleGenerator.pkl | 59 +++++++--- .../k8s.contrib.crd/tests/ModuleGenerator.pkl | 27 +++-- .../tests/ModuleGenerator.pkl-expected.pcf | 80 +++++++++++++- .../tests/fixtures/crds_conflict.yaml | 81 ++++++++++++++ packages/org.json_schema.contrib/PklProject | 4 +- .../internal/ModuleGenerator.pkl | 37 ++++++- .../internal/utils.pkl | 6 +- .../tests/ModuleGenerator.pkl | 42 ++++---- .../tests/ModuleGenerator.pkl-expected.pcf | 102 ++++++++++++++---- .../tests/{ => fixtures}/test_allOf.json | 0 .../tests/fixtures/test_conflicts.json | 86 +++++++++++++++ 13 files changed, 454 insertions(+), 76 deletions(-) create mode 100644 packages/k8s.contrib.crd/tests/fixtures/crds_conflict.yaml rename packages/org.json_schema.contrib/tests/{ => fixtures}/test_allOf.json (100%) create mode 100644 packages/org.json_schema.contrib/tests/fixtures/test_conflicts.json diff --git a/packages/k8s.contrib.crd/PklProject b/packages/k8s.contrib.crd/PklProject index a456701..cfcc938 100644 --- a/packages/k8s.contrib.crd/PklProject +++ b/packages/k8s.contrib.crd/PklProject @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,5 +29,5 @@ dependencies { } package { - version = "1.0.14" + version = "2.0.0" } diff --git a/packages/k8s.contrib.crd/PklProject.deps.json b/packages/k8s.contrib.crd/PklProject.deps.json index 71e8cc4..5c0c7e3 100644 --- a/packages/k8s.contrib.crd/PklProject.deps.json +++ b/packages/k8s.contrib.crd/PklProject.deps.json @@ -15,7 +15,7 @@ }, "package://pkg.pkl-lang.org/pkl-pantry/org.json_schema.contrib@1": { "type": "local", - "uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/org.json_schema.contrib@1.1.1", + "uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/org.json_schema.contrib@1.1.2", "path": "../org.json_schema.contrib" }, "package://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.syntax@1": { diff --git a/packages/k8s.contrib.crd/internal/ModuleGenerator.pkl b/packages/k8s.contrib.crd/internal/ModuleGenerator.pkl index 4662bfb..184727d 100644 --- a/packages/k8s.contrib.crd/internal/ModuleGenerator.pkl +++ b/packages/k8s.contrib.crd/internal/ModuleGenerator.pkl @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import "pkl:reflect" import "@jsonschema.contrib/internal/Type.pkl" import "@jsonschema.contrib/internal/TypesGenerator.pkl" import "@jsonschema.contrib/internal/utils.pkl" +import "@jsonschema.contrib/internal/singularize.pkl" import "@jsonschema/JsonSchema.pkl" import "@jsonschema/Parser.pkl" import "@k8s/apiextensions-apiserver/pkg/apis/apiextensions/v1/CustomResourceDefinition.pkl" @@ -54,7 +55,7 @@ local schema: CustomResourceDefinition.CustomResourceValidation|BetaCRD.CustomRe (if (crd is BetaCRD) version.schema ?? crd.spec.validation else version.schema)!! /// The Schema -rootSchema: JsonSchema((s) -> validCRDSchema(s)) = Parser.parse(new JsonRenderer {}.renderDocument(schema.openAPIV3Schema)) as JsonSchema +rootSchema: JsonSchema(validCRDSchema(this)) = Parser.parse(new JsonRenderer {}.renderDocument(schema.openAPIV3Schema)) as JsonSchema local ignoreProperties = Set("apiVersion", "kind", "metadata") local filteredRootSchema = (rootSchema) { @@ -280,31 +281,59 @@ function isClassLike(schema: JsonSchema.Schema): Boolean = /// /// Try to use the parent property's name as part of the class name in case of conflict. /// If already at the root, add a number at the end. -local function determineTypeName(path: List, candidateName: String, existingTypeNames: Set, index: Int): Type = - if (existingTypeNames.contains(utils.pascalCase(candidateName))) - if (path.isEmpty) - determineTypeName(path, candidateName + index.toString(), existingTypeNames, index + 1) +local function determineTypeName( + path: List, + candidateName: String, + existingTypeNames: Set, + index: Int +): Type = + let (candidate = utils.pascalCase(candidateName)) + if (existingTypeNames.findOrNull((it) -> it.name == candidate) != null) + if (path.isEmpty) + determineTypeName( + path, + candidateName + index.toString(), + existingTypeNames, + index + 1 + ) + else + let (newPath = dropLast(path)) + determineTypeName( + newPath, + getCandidateName(newPath) + candidate, + existingTypeNames, + index + ) else - determineTypeName( - path.dropLast(1), - utils.pascalCase(path.last.capitalize()) + utils.pascalCase(candidateName), - existingTypeNames, - index - ) + new { name = candidate; moduleName = module.moduleName } + +// noinspection TypeMismatch +local function getCandidateName(path: List) = + if (path.isEmpty) + "Item" + else if (path.last == "[]") + path.dropLast(1).lastOrNull?.ifNonNull((it) -> utils.pascalCase(singularize.singularize(it))) ?? "Item" + else + utils.pascalCase(path.last) + +local function dropLast(path: List) = + if (path.last == "[]") + path.dropLast(2) else - new { name = utils.pascalCase(candidateName); moduleName = module.moduleName } + path.dropLast(1) /// The schemas that should be rendered as classes. /// /// Classes get rendered for any subschema that has [JsonSchema.properties] defined, and does not show up in converters local classSchemas: Type.TypeNames = utils._findMatchingSubSchemas(filteredRootSchema, List(), (elem) -> elem != filteredRootSchema && isClassLike(elem)) - .filter((path, _) -> !pathPrefixes(path).any((prefix) -> converters.containsKey(prefix))) // path or prefix are not explicitly in converters + // path or prefix are not explicitly in converters + .filter((path, _) -> !pathPrefixes(path).any((prefix) -> converters.containsKey(prefix))) .entries .fold(Map(), (accumulator: Type.TypeNames, pair) -> let (path = pair.first) let (schema = pair.second) - let (typeName = determineTypeName(path, path.lastOrNull?.capitalize() ?? "Item", accumulator.values.toSet(), 0)) + let (typeName = determineTypeName(path, getCandidateName(path), accumulator.values.toSet(), 0)) accumulator.put(schema, typeName) ) diff --git a/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl b/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl index 5dbdc5a..f259e8d 100644 --- a/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl +++ b/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,9 +17,9 @@ module k8s.contrib.crd.tests.ModuleGenerator amends "pkl:test" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/ResourceRequirements.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/EnvVar.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/networking/v1/NetworkPolicy.pkl" +import "@k8s/api/core/v1/ResourceRequirements.pkl" +import "@k8s/api/core/v1/EnvVar.pkl" +import "@k8s/api/networking/v1/NetworkPolicy.pkl" import "../generate.pkl" @@ -28,11 +28,11 @@ local generator = (generate) { source = "dummy://test_uri" converters { ["restateclusters.restate.dev"] { - [List("spec", "compute", "env", "env")] = EnvVar + [List("spec", "compute", "env", "[]")] = EnvVar [List("spec", "compute", "resources")] = ResourceRequirements - [List("spec", "security", "networkPeers", "ingress", "ingres")] = NetworkPolicy.NetworkPolicyPeer - [List("spec", "security", "networkPeers", "admin", "admin")] = NetworkPolicy.NetworkPolicyPeer - [List("spec", "security", "networkPeers", "metrics", "metric")] = NetworkPolicy.NetworkPolicyPeer + [List("spec", "security", "networkPeers", "ingress", "[]")] = NetworkPolicy.NetworkPolicyPeer + [List("spec", "security", "networkPeers", "admin", "[]")] = NetworkPolicy.NetworkPolicyPeer + [List("spec", "security", "networkPeers", "metrics", "[]")] = NetworkPolicy.NetworkPolicyPeer } } } @@ -59,7 +59,16 @@ examples { } for (filename, value in generator3.output.files!!) { - ["\(filename).pkl -- different version of k8s"] { + ["\(filename) -- different version of k8s"] { + value.text + } + } + + ["conflicting schemas"] { + for (_, value in (generate) { + sourceContents = read("fixtures/crds_conflict.yaml") + source = "dummy://test_uri" + }.output.files!!) { value.text } } diff --git a/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl-expected.pcf b/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl-expected.pcf index 2161933..449fa3e 100644 --- a/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl-expected.pcf +++ b/packages/k8s.contrib.crd/tests/ModuleGenerator.pkl-expected.pcf @@ -197,7 +197,7 @@ examples { """ } - ["RestateCluster.pkl.pkl -- different version of k8s"] { + ["RestateCluster.pkl -- different version of k8s"] { """ /// Auto-generated derived type for RestateClusterSpec via `CustomResource` /// @@ -294,6 +294,84 @@ examples { storageRequestBytes: Int(this >= 1.0) } + """ + } + ["conflicting schemas"] { + """ + /// This module was generated from the CustomResourceDefinition at . + module bar.foo.v1.FooBar + + extends "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/K8sResource.pkl" + + import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/apimachinery/pkg/apis/meta/v1/ObjectMeta.pkl" + + fixed apiVersion: "foo.bar/v1" + + fixed kind: "FooBar" + + /// Standard object's metadata. + /// + /// More info: . + metadata: ObjectMeta? + + foo: Foo? + + class Foo { + /// Test nested objects field collision. + redObject: RedObject? + + /// Test nested objects field collision. + blueObject: BlueObject? + } + + /// Test nested objects field collision. + class RedObject { + /// Nested field. + nestedField: String + + /// Nested child object. + nestedObject: NestedObject? + + /// Nested list object red. + nestedList: Listing? + } + + /// Nested child object. + class NestedObject { + /// Nested field. + nestedRed: String? + } + + /// Red nested object test items. + class NestedList { + /// Red nested list field. + nestedListItemField: String? + } + + /// Test nested objects field collision. + class BlueObject { + /// Nested field. + nestedField: String + + /// Nested child object. + nestedObject: BlueObjectNestedObject? + + /// Nested list object blue. + nestedList: Listing? + } + + /// Nested child object. + class BlueObjectNestedObject { + /// Nested field. + nestedBlue: String? + } + + /// Blue nested object test items. + class BlueObjectNestedList { + /// Blue nested list field. + nestedListItemField: String? + } + """ } } diff --git a/packages/k8s.contrib.crd/tests/fixtures/crds_conflict.yaml b/packages/k8s.contrib.crd/tests/fixtures/crds_conflict.yaml new file mode 100644 index 0000000..f636fe7 --- /dev/null +++ b/packages/k8s.contrib.crd/tests/fixtures/crds_conflict.yaml @@ -0,0 +1,81 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: foo.bar.com +spec: + group: foo.bar + names: + categories: [] + kind: FooBar + plural: foobar + shortNames: + - fb + singular: foobar + scope: Cluster + versions: + - additionalPrinterColumns: [] + name: v1 + schema: + openAPIV3Schema: + properties: + foo: + properties: + redObject: + description: Test nested objects field collision. + properties: + nestedField: + description: Nested field. + type: string + nestedObject: + description: Nested child object. + properties: + nestedRed: + description: Nested field. + type: string + type: object + nestedList: + description: Nested list object red. + items: + description: Red nested object test items. + properties: + nestedListItemField: + description: Red nested list field. + type: string + type: object + nullable: true + type: array + required: + - nestedField + type: object + blueObject: + description: Test nested objects field collision. + properties: + nestedField: + description: Nested field. + type: string + nestedObject: + description: Nested child object. + properties: + nestedBlue: + description: Nested field. + type: string + type: object + nestedList: + description: Nested list object blue. + items: + description: Blue nested object test items. + properties: + nestedListItemField: + description: Blue nested list field. + type: string + type: object + nullable: true + type: array + required: + - nestedField + type: object + required: + - image + type: object + served: true + storage: true \ No newline at end of file diff --git a/packages/org.json_schema.contrib/PklProject b/packages/org.json_schema.contrib/PklProject index 95eb03c..09089c7 100644 --- a/packages/org.json_schema.contrib/PklProject +++ b/packages/org.json_schema.contrib/PklProject @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,5 +25,5 @@ dependencies { } package { - version = "1.1.1" + version = "1.1.2" } diff --git a/packages/org.json_schema.contrib/internal/ModuleGenerator.pkl b/packages/org.json_schema.contrib/internal/ModuleGenerator.pkl index 12f5ebc..72836f7 100644 --- a/packages/org.json_schema.contrib/internal/ModuleGenerator.pkl +++ b/packages/org.json_schema.contrib/internal/ModuleGenerator.pkl @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import "utils.pkl" import "TypesGenerator.pkl" import "../ref.pkl" import "Type.pkl" +import "singularize.pkl" local pcfRenderer = new PcfRenderer { useCustomStringDelimiters = true } local jsonRenderer = new JsonRenderer {} @@ -215,22 +216,50 @@ local function determineTypeName(path: List, candidateName: String, exis if (path.isEmpty) determineTypeName(path, candidateName + index.toString(), existingTypeNames, index + 1) else - determineTypeName(path.dropLast(1), utils.pascalCase(path.last.capitalize()) + utils.pascalCase(candidateName), existingTypeNames, index) + let (newPath = dropLast(path)) + determineTypeName( + newPath, + getCandidateName(newPath) + utils.pascalCase(candidateName), + existingTypeNames, + index + ) else candidateType +// noinspection TypeMismatch +local function getCandidateName(path: List) = + if (path.isEmpty) + "Item" + else if (path.last == "[]") + path.dropLast(1).lastOrNull?.ifNonNull((it) -> utils.pascalCase(singularize.singularize(it))) ?? "Item" + else + utils.pascalCase(path.last) + +local function dropLast(path: List) = + if (path.last == "[]") + path.dropLast(2) + else + path.dropLast(1) + /// The schemas that should be rendered as classes. /// /// Classes get rendered for any subschema that has [JsonSchema.properties] defined, and does not have [JsonSchema.`$ref`] defined. local classSchemas: Type.TypeNames = let (schemas = utils._findMatchingSubSchemas(rootSchema, List(), (elem) -> elem != rootSchema && isClassLike(elem))) - let (collatedSchemas = if (collatedRootSchema == rootSchema) Map() else utils._findMatchingSubSchemas(collatedRootSchema, List(), (elem) -> elem != collatedRootSchema && isClassLike(elem))) + let (collatedSchemas = + if (collatedRootSchema == rootSchema) Map() + else utils._findMatchingSubSchemas( + collatedRootSchema, + List(), + (elem) -> elem != collatedRootSchema && isClassLike(elem) + ) + ) (schemas + collatedSchemas) .entries .fold(Map(), (accumulator: Type.TypeNames, pair) -> let (path = pair.first) let (schema = pair.second) - let (typeName = determineTypeName(path.dropLast(1), path.lastOrNull?.capitalize() ?? "Item", accumulator.values.toSet(), 0)) + let (typeName = determineTypeName(path, getCandidateName(path), accumulator.values.toSet(), 0)) accumulator.put(schema, typeName) ) diff --git a/packages/org.json_schema.contrib/internal/utils.pkl b/packages/org.json_schema.contrib/internal/utils.pkl index 921835f..4c7fabd 100644 --- a/packages/org.json_schema.contrib/internal/utils.pkl +++ b/packages/org.json_schema.contrib/internal/utils.pkl @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import "@syntax/operators.pkl" import "@syntax/AnnotationNode.pkl" import "Type.pkl" import "@jsonschema/JsonSchema.pkl" -import "singularize.pkl" /// Renders the string in pascal case. /// @@ -160,8 +159,7 @@ function _findMatchingSubSchemas( else Map()) + (if (schema.items is JsonSchema) - // Singularize the name so that we get type names like `Listing` instead of `Listing` - _findMatchingSubSchemas(schema.items as JsonSchema, if (path.length > 0) path.add(singularize.singularize(path.last)) else path, predicate) + _findMatchingSubSchemas(schema.items as JsonSchema, path.add("[]"), predicate) else if (schema.items is Listing) findMatchingSchemasInListing(schema.items as Listing, path, "Item", predicate) else Map()) diff --git a/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl b/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl index 66aec3c..6172b06 100644 --- a/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl +++ b/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl @@ -1,5 +1,5 @@ //===----------------------------------------------------------------------===// -// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ examples { } new ModuleGenerator { rootSchema = schema - moduleName = "com.apple.Example" + moduleName = "com.Example" }.moduleNode.render("") } ["required properties"] { @@ -57,7 +57,7 @@ examples { "bar" } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["deprecated properties"] { local schema: JsonSchema = new { @@ -70,7 +70,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["doc comments"] { local schema: JsonSchema = new { @@ -86,7 +86,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes within properties"] { local schema: JsonSchema = new { @@ -102,7 +102,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes in nested properties"] { local schema: JsonSchema = new { @@ -123,7 +123,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes in nested additionalProperties"] { local schema: JsonSchema = new { @@ -151,7 +151,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes in nested patternProperties"] { local schema: JsonSchema = new { @@ -182,7 +182,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes in listings"] { local schema: JsonSchema = new { @@ -200,7 +200,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes in definitions"] { local schema: JsonSchema = new { @@ -222,7 +222,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes within definitions properties"] { local schema: JsonSchema = new { @@ -263,7 +263,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["classes from inline positions"] { local schema: JsonSchema = new { @@ -290,7 +290,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["typealiases in definitions"] { local schema: JsonSchema = new { @@ -309,7 +309,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["typealiases in $defs"] { local schema: JsonSchema = new { @@ -328,7 +328,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["typealiases in subschema definitions"] { local schema: JsonSchema = new { @@ -351,7 +351,7 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["root schemas that are not objects"] { local schema: JsonSchema = new { @@ -365,10 +365,14 @@ examples { } } } - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } ["allOf"] { - local schema = Parser.parse(read("./test_allOf.json")) as JsonSchema - new ModuleGenerator { rootSchema = schema; moduleName = "com.apple.Example" }.moduleNode.render("") + local schema = Parser.parse(read("fixtures/test_allOf.json")) as JsonSchema + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") + } + ["conflicts"] { + local schema = Parser.parse(read("fixtures/test_conflicts.json")) as JsonSchema + new ModuleGenerator { rootSchema = schema; moduleName = "com.Example" }.moduleNode.render("") } } diff --git a/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl-expected.pcf b/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl-expected.pcf index d381ad7..cb05199 100644 --- a/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl-expected.pcf +++ b/packages/org.json_schema.contrib/tests/ModuleGenerator.pkl-expected.pcf @@ -2,7 +2,7 @@ examples { ["basic"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: String? @@ -13,7 +13,7 @@ examples { ["required properties"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: String @@ -24,7 +24,7 @@ examples { ["deprecated properties"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example @Deprecated foo: String? @@ -38,7 +38,7 @@ examples { /// FooBars when foo can bar /// /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example /// The fooiest of foos /// @@ -52,7 +52,7 @@ examples { /// FooBar /// /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -65,7 +65,7 @@ examples { ["classes in nested properties"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -82,7 +82,7 @@ examples { ["classes in nested additionalProperties"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Mapping? @@ -99,7 +99,7 @@ examples { ["classes in nested patternProperties"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Mapping? @@ -116,7 +116,7 @@ examples { ["classes in listings"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foos: Listing? @@ -129,7 +129,7 @@ examples { ["classes in definitions"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -142,7 +142,7 @@ examples { ["classes within definitions properties"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -169,7 +169,7 @@ examples { ["classes from inline positions"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Myfoo? @@ -185,7 +185,7 @@ examples { ["typealiases in definitions"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -196,7 +196,7 @@ examples { ["typealiases in $defs"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -207,7 +207,7 @@ examples { ["typealiases in subschema definitions"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example foo: Foo? @@ -224,7 +224,7 @@ examples { /// This module was generated from JSON Schema from <>. /// /// WARN: The root schema's type is `"array"`, and cannot be correctly mapped to a Pkl module. - module com.apple.Example + module com.Example class Item { foo: String? @@ -235,9 +235,9 @@ examples { ["allOf"] { """ /// This module was generated from JSON Schema from <>. - module com.apple.Example + module com.Example - growpart: Growpart0? + growpart: ItemGrowpart? groups: (String(!isEmpty)|Listing(!isEmpty))? @@ -442,7 +442,7 @@ examples { ignore_growroot_disabled: Boolean? } - class Growpart0 { + class ItemGrowpart { /// The utility to use for resizing. Default: ``auto`` /// /// Possible options: @@ -475,6 +475,70 @@ examples { typealias UsersGroupsGroupsByGroupname = Mapping(!isEmpty)> + """ + } + ["conflicts"] { + """ + /// This module was generated from JSON Schema from <>. + /// + /// WARN: The root schema describes open-ended properties, but this is not possible to describe at the + /// module level. + module com.Example + + /// Test nested objects field collision. + redObject: RedObject? + + /// Test nested objects field collision. + blueObject: BlueObject? + + /// Test nested objects field collision. + class RedObject { + /// Nested field. + nestedField: String + + /// Nested child object. + nestedObject: NestedObject? + + /// Nested list object red. + nestedList: Listing? + } + + /// Nested child object. + class NestedObject { + /// Nested field. + nestedRed: String? + } + + /// Red nested object test items. + class NestedList { + /// Red nested list field. + nestedListItemField: String? + } + + /// Test nested objects field collision. + class BlueObject { + /// Nested field. + nestedField: String + + /// Nested child object. + nestedObject: BlueObjectNestedObject? + + /// Nested list object blue. + nestedList: Listing? + } + + /// Nested child object. + class BlueObjectNestedObject { + /// Nested field. + nestedBlue: String? + } + + /// Blue nested object test items. + class BlueObjectNestedList { + /// Blue nested list field. + nestedListItemField: String? + } + """ } } diff --git a/packages/org.json_schema.contrib/tests/test_allOf.json b/packages/org.json_schema.contrib/tests/fixtures/test_allOf.json similarity index 100% rename from packages/org.json_schema.contrib/tests/test_allOf.json rename to packages/org.json_schema.contrib/tests/fixtures/test_allOf.json diff --git a/packages/org.json_schema.contrib/tests/fixtures/test_conflicts.json b/packages/org.json_schema.contrib/tests/fixtures/test_conflicts.json new file mode 100644 index 0000000..edbb6e0 --- /dev/null +++ b/packages/org.json_schema.contrib/tests/fixtures/test_conflicts.json @@ -0,0 +1,86 @@ +{ + "$comment": "This is a minimized subset of the schema from https://github.com/canonical/cloud-init/blob/main/cloudinit/config/schemas/schema-cloud-config-v1.json. This is derived from the original project under the terms of the Apache 2.0 license.", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "redObject": { + "description": "Test nested objects field collision.", + "properties": { + "nestedField": { + "description": "Nested field.", + "type": "string" + }, + "nestedObject": { + "description": "Nested child object.", + "properties": { + "nestedRed": { + "description": "Nested field.", + "type": "string" + } + }, + "type": "object" + }, + "nestedList": { + "description": "Nested list object red.", + "items": { + "description": "Red nested object test items.", + "properties": { + "nestedListItemField": { + "description": "Red nested list field.", + "type": "string" + } + }, + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "nestedField" + ], + "type": "object" + }, + "blueObject": { + "description": "Test nested objects field collision.", + "properties": { + "nestedField": { + "description": "Nested field.", + "type": "string" + }, + "nestedObject": { + "description": "Nested child object.", + "properties": { + "nestedBlue": { + "description": "Nested field.", + "type": "string" + } + }, + "type": "object" + }, + "nestedList": { + "description": "Nested list object blue.", + "items": { + "description": "Blue nested object test items.", + "properties": { + "nestedListItemField": { + "description": "Blue nested list field.", + "type": "string" + } + }, + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "nestedField" + ], + "type": "object" + } + }, + "required": [ + "image" + ] +}