diff --git a/pkg/gen/dotnet-templates/kind.cs.mustache b/pkg/gen/dotnet-templates/kind.cs.mustache index fedbb51501..d8f2752012 100644 --- a/pkg/gen/dotnet-templates/kind.cs.mustache +++ b/pkg/gen/dotnet-templates/kind.cs.mustache @@ -15,7 +15,7 @@ namespace Pulumi.Kubernetes.{{Group}}.{{Version}} {{#Properties}} {{{Comment}}} [Output("{{Name}}")] - public {{{PropType}}} {{LanguageName}} { get; private set; } = null!; + public {{{ProviderType}}} {{LanguageName}} { get; private set; } = null!; {{/Properties}} diff --git a/pkg/gen/dotnet-templates/typesInput.cs.mustache b/pkg/gen/dotnet-templates/typesInput.cs.mustache index 89b18e5c06..e4f71c6883 100644 --- a/pkg/gen/dotnet-templates/typesInput.cs.mustache +++ b/pkg/gen/dotnet-templates/typesInput.cs.mustache @@ -20,38 +20,38 @@ namespace Pulumi.Kubernetes.Types.Inputs.{{Group}} {{#RequiredInputProperties}} {{#DotnetIsListOrMap}} [Input("{{Name}}", required: true)] - private {{{PropType}}}? _{{Name}}; + private {{{InputsAPIType}}}? _{{Name}}; {{{Comment}}} - public {{{PropType}}} {{LanguageName}} + public {{{InputsAPIType}}} {{LanguageName}} { - get => _{{Name}} ?? (_{{Name}} = new {{{PropType}}}()); + get => _{{Name}} ?? (_{{Name}} = new {{{InputsAPIType}}}()); set => _{{Name}} = value; } {{/DotnetIsListOrMap}} {{^DotnetIsListOrMap}} {{{Comment}}} [Input("{{Name}}", required: true)] - public {{{PropType}}} {{LanguageName}} { get; set; } = null!; + public {{{InputsAPIType}}} {{LanguageName}} { get; set; } = null!; {{/DotnetIsListOrMap}} {{/RequiredInputProperties}} {{#OptionalInputProperties}} {{#DotnetIsListOrMap}} [Input("{{Name}}")] - private {{{PropType}}}? _{{Name}}; + private {{{InputsAPIType}}}? _{{Name}}; {{{Comment}}} - public {{{PropType}}} {{LanguageName}} + public {{{InputsAPIType}}} {{LanguageName}} { - get => _{{Name}} ?? (_{{Name}} = new {{{PropType}}}()); + get => _{{Name}} ?? (_{{Name}} = new {{{InputsAPIType}}}()); set => _{{Name}} = value; } {{/DotnetIsListOrMap}} {{^DotnetIsListOrMap}} {{{Comment}}} [Input("{{Name}}")] - public {{{PropType}}}? {{LanguageName}} { get; set; } + public {{{InputsAPIType}}}? {{LanguageName}} { get; set; } {{/DotnetIsListOrMap}} {{/OptionalInputProperties}} diff --git a/pkg/gen/dotnet-templates/typesOutput.cs.mustache b/pkg/gen/dotnet-templates/typesOutput.cs.mustache index 45c821177e..abcfde4ef7 100644 --- a/pkg/gen/dotnet-templates/typesOutput.cs.mustache +++ b/pkg/gen/dotnet-templates/typesOutput.cs.mustache @@ -20,13 +20,13 @@ namespace Pulumi.Kubernetes.Types.Outputs.{{Group}} { {{#Properties}} {{{Comment}}} - public readonly {{{PropType}}} {{LanguageName}}; + public readonly {{{OutputsAPIType}}} {{LanguageName}}; {{/Properties}} [OutputConstructor] private {{Kind}}( {{#Properties}} - {{{PropType}}} {{DotnetVarName}}{{^IsLast}},{{/IsLast}}{{#IsLast}}){{/IsLast}} + {{{OutputsAPIType}}} {{DotnetVarName}}{{^IsLast}},{{/IsLast}}{{#IsLast}}){{/IsLast}} {{/Properties}} { {{#Properties}} diff --git a/pkg/gen/dotnet.go b/pkg/gen/dotnet.go index 0ef41a3304..5049780963 100644 --- a/pkg/gen/dotnet.go +++ b/pkg/gen/dotnet.go @@ -80,32 +80,28 @@ func DotnetClient( ) (inputsts, outputsts string, groups map[string]string, err error) { definitions := swagger["definitions"].(map[string]interface{}) - inputGroupsSlice := createGroups(definitions, dotnetInputs()) + groupsSlice := createGroups(definitions, dotnetOpts()) + inputsts, err = mustache.RenderFile(fmt.Sprintf("%s/typesInput.cs.mustache", templateDir), map[string]interface{}{ - "Groups": inputGroupsSlice, + "Groups": groupsSlice, }) if err != nil { return } - outputGroupsSlice := createGroups(definitions, dotnetOutputs()) outputsts, err = mustache.RenderFile(fmt.Sprintf("%s/typesOutput.cs.mustache", templateDir), map[string]interface{}{ - "Groups": outputGroupsSlice, + "Groups": groupsSlice, }) if err != nil { return } - groupsSlice := createGroups(definitions, dotnetProvider()) - fmt.Printf("%v\n", groupsSlice) - groups = make(map[string]string) - for _, group := range groupsSlice { for _, version := range group.Versions() { - for _, kind := range version.Kinds() { + for _, kind := range version.TopLevelKinds() { inputMap := map[string]interface{}{ "RawAPIVersion": kind.RawAPIVersion(), "Comment": kind.Comment(), diff --git a/pkg/gen/nodejs-templates/kind.ts.mustache b/pkg/gen/nodejs-templates/kind.ts.mustache index 262dee90c9..ae89cc149e 100644 --- a/pkg/gen/nodejs-templates/kind.ts.mustache +++ b/pkg/gen/nodejs-templates/kind.ts.mustache @@ -13,7 +13,7 @@ import { getVersion } from "../../version"; export class {{Kind}} extends pulumi.CustomResource { {{#Properties}} {{{Comment}}} - public readonly {{Name}}: {{{PropType}}}; + public readonly {{Name}}: {{{ProviderType}}}; {{/Properties}} /** diff --git a/pkg/gen/nodejs-templates/providerIndex.ts.mustache b/pkg/gen/nodejs-templates/providerIndex.ts.mustache index 8f3bddf617..1773d67db5 100644 --- a/pkg/gen/nodejs-templates/providerIndex.ts.mustache +++ b/pkg/gen/nodejs-templates/providerIndex.ts.mustache @@ -8,11 +8,13 @@ export { helm, yaml }; // Import groups {{#Groups}} +{{#HasTopLevelKinds}} import * as {{Group}} from "./{{Group}}/index"; +{{/HasTopLevelKinds}} {{/Groups}} // Export sub-modules -export { {{#Groups}}{{Group}}, {{/Groups}} }; +export { {{#Groups}}{{#HasTopLevelKinds}}{{Group}}, {{/HasTopLevelKinds}}{{/Groups}} }; // Import and export sub-modules for all Kubernetes types. import * as types from "./types"; diff --git a/pkg/gen/nodejs-templates/typesInput.ts.mustache b/pkg/gen/nodejs-templates/typesInput.ts.mustache index 68490a7819..c5231fb8fe 100644 --- a/pkg/gen/nodejs-templates/typesInput.ts.mustache +++ b/pkg/gen/nodejs-templates/typesInput.ts.mustache @@ -15,12 +15,12 @@ export namespace {{Group}} { export interface {{Kind}} { {{#RequiredInputProperties}} {{{Comment}}} - {{Name}}: {{{PropType}}} + {{Name}}: {{{InputsAPIType}}} {{/RequiredInputProperties}} {{#OptionalInputProperties}} {{{Comment}}} - {{Name}}?: {{{PropType}}} + {{Name}}?: {{{InputsAPIType}}} {{/OptionalInputProperties}} } diff --git a/pkg/gen/nodejs-templates/typesOutput.ts.mustache b/pkg/gen/nodejs-templates/typesOutput.ts.mustache index a493f513c0..6f7bb8fadf 100644 --- a/pkg/gen/nodejs-templates/typesOutput.ts.mustache +++ b/pkg/gen/nodejs-templates/typesOutput.ts.mustache @@ -12,7 +12,7 @@ export namespace {{Group}} { export interface {{Kind}} { {{#Properties}} {{{Comment}}} - readonly {{Name}}: {{{PropType}}} + readonly {{Name}}: {{{OutputsAPIType}}} {{/Properties}} } diff --git a/pkg/gen/nodejs-templates/yaml.ts.mustache b/pkg/gen/nodejs-templates/yaml.ts.mustache index 768036f519..f3a19e4118 100644 --- a/pkg/gen/nodejs-templates/yaml.ts.mustache +++ b/pkg/gen/nodejs-templates/yaml.ts.mustache @@ -212,10 +212,10 @@ import * as outputs from "../types/output"; */ {{#Groups}} {{#Versions}} - {{#KindsAndAliases}} + {{#TopLevelKindsAndAliases}} public getResource(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", name: string): pulumi.Output; public getResource(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", namespace: string, name: string): pulumi.Output; - {{/KindsAndAliases}} + {{/TopLevelKindsAndAliases}} {{/Versions}} {{/Groups}} public getResource(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.Output { @@ -230,12 +230,12 @@ import * as outputs from "../types/output"; */ {{#Groups}} {{#Versions}} - {{#KindsAndAliases}} + {{#TopLevelKindsAndAliases}} {{#Properties}} - public getResourceProperty(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", name: string, property: "{{LanguageName}}"): {{{PropType}}}; - public getResourceProperty(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", namespace: string, name: string, property: "{{LanguageName}}"): {{{PropType}}}; + public getResourceProperty(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", name: string, property: "{{LanguageName}}"): {{{ProviderType}}}; + public getResourceProperty(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", namespace: string, name: string, property: "{{LanguageName}}"): {{{ProviderType}}}; {{/Properties}} - {{/KindsAndAliases}} + {{/TopLevelKindsAndAliases}} {{/Versions}} {{/Groups}} public getResourceProperty(groupVersionKind: string, namespaceOrName: string, nameOrProperty: string, property?: string): pulumi.Output { @@ -400,9 +400,9 @@ import * as outputs from "../types/output"; (apiVersion == "v1" && kind == "List") {{#Groups}} {{#Versions}} - {{#ListKindsAndAliases}} + {{#ListTopLevelKindsAndAliases}} || (apiVersion == "{{RawAPIVersion}}" && kind == "{{Kind}}") - {{/ListKindsAndAliases}} + {{/ListTopLevelKindsAndAliases}} {{/Versions}} {{/Groups}} ) { @@ -430,13 +430,13 @@ import * as outputs from "../types/output"; switch (`${apiVersion}/${kind}`) { {{#Groups}} {{#Versions}} - {{#KindsAndAliases}} + {{#TopLevelKindsAndAliases}} case "{{RawAPIVersion}}/{{Kind}}": return [id.apply(id => ({ name: `{{RawAPIVersion}}/{{Kind}}::${id}`, resource: new k8s.{{Group}}.{{Version}}.{{Kind}}(id, obj, opts), }))]; - {{/KindsAndAliases}} + {{/TopLevelKindsAndAliases}} {{/Versions}} {{/Groups}} default: diff --git a/pkg/gen/nodejs.go b/pkg/gen/nodejs.go index 344d703415..2612c02e47 100644 --- a/pkg/gen/nodejs.go +++ b/pkg/gen/nodejs.go @@ -42,7 +42,8 @@ func NodeJSClient(swagger map[string]interface{}, templateDir string, ) (inputsts, outputsts, indexts, yamlts, packagejson string, groupsts map[string]*GroupTS, err error) { definitions := swagger["definitions"].(map[string]interface{}) - groupsSlice := createGroups(definitions, nodeJSInputs()) + groupsSlice := createGroups(definitions, nodeJSOpts()) + inputsts, err = mustache.RenderFile(fmt.Sprintf("%s/typesInput.ts.mustache", templateDir), map[string]interface{}{ "Groups": groupsSlice, @@ -51,7 +52,6 @@ func NodeJSClient(swagger map[string]interface{}, templateDir string, return } - groupsSlice = createGroups(definitions, nodeJSOutputs()) outputsts, err = mustache.RenderFile(fmt.Sprintf("%s/typesOutput.ts.mustache", templateDir), map[string]interface{}{ "Groups": groupsSlice, @@ -60,16 +60,23 @@ func NodeJSClient(swagger map[string]interface{}, templateDir string, return } - groupsSlice = createGroups(definitions, nodeJSProvider()) groupsts = make(map[string]*GroupTS) for _, group := range groupsSlice { + if !group.HasTopLevelKinds() { + continue + } + groupTS := &GroupTS{} for _, version := range group.Versions() { + if !version.HasTopLevelKinds() { + continue + } + if groupTS.Versions == nil { groupTS.Versions = make(map[string]*VersionTS) } versionTS := &VersionTS{} - for _, kind := range version.Kinds() { + for _, kind := range version.TopLevelKinds() { if versionTS.Kinds == nil { versionTS.Kinds = make(map[string]string) } @@ -103,7 +110,7 @@ func NodeJSClient(swagger map[string]interface{}, templateDir string, kindIndexTS, err := mustache.RenderFile(fmt.Sprintf("%s/kindIndex.ts.mustache", templateDir), map[string]interface{}{ - "Kinds": version.Kinds(), + "Kinds": version.TopLevelKinds(), }) if err != nil { return "", "", "", "", "", nil, err diff --git a/pkg/gen/python-templates/kind.py.mustache b/pkg/gen/python-templates/kind.py.mustache index df70e8e971..79a0656dd2 100644 --- a/pkg/gen/python-templates/kind.py.mustache +++ b/pkg/gen/python-templates/kind.py.mustache @@ -31,7 +31,7 @@ class {{Kind}}(pulumi.CustomResource): """ {{#Properties}} - {{LanguageName}}: {{{PropType}}} + {{LanguageName}}: {{{ProviderType}}} {{{Comment}}} {{/Properties}} @@ -42,10 +42,10 @@ class {{Kind}}(pulumi.CustomResource): :param str resource_name: The _unique_ name of the resource. :param pulumi.ResourceOptions opts: A bag of options that control this resource's behavior. {{#RequiredInputProperties}} - :param {{{PythonConstructorPropType}}} {{LanguageName}}: {{{PythonConstructorComment}}} + :param {{{InputsAPIType}}} {{LanguageName}}: {{{PythonConstructorComment}}} {{/RequiredInputProperties}} {{#OptionalInputProperties}} - :param {{{PythonConstructorPropType}}} {{LanguageName}}: {{{PythonConstructorComment}}} + :param {{{InputsAPIType}}} {{LanguageName}}: {{{PythonConstructorComment}}} {{/OptionalInputProperties}} """ if __name__ is not None: diff --git a/pkg/gen/python-templates/root__init__.py.mustache b/pkg/gen/python-templates/root__init__.py.mustache index 433898b82a..054f0ac358 100644 --- a/pkg/gen/python-templates/root__init__.py.mustache +++ b/pkg/gen/python-templates/root__init__.py.mustache @@ -5,7 +5,9 @@ # Make subpackages available: __all__ = [ {{#Groups}} + {{#HasTopLevelKinds}} "{{Group}}", + {{/HasTopLevelKinds}} {{/Groups}} "helm", "provider", diff --git a/pkg/gen/python-templates/version__init__.py.mustache b/pkg/gen/python-templates/version__init__.py.mustache index 279878a42c..f72dcb152c 100644 --- a/pkg/gen/python-templates/version__init__.py.mustache +++ b/pkg/gen/python-templates/version__init__.py.mustache @@ -3,6 +3,6 @@ # *** Do not edit by hand unless you're certain you know what you are doing! *** # Export this package's modules as members: -{{#Kinds}} +{{#TopLevelKinds}} from .{{Kind}} import ({{Kind}}) -{{/Kinds}} +{{/TopLevelKinds}} diff --git a/pkg/gen/python-templates/yaml.py.mustache b/pkg/gen/python-templates/yaml.py.mustache index 00cb7f6084..ed62d9ebcc 100644 --- a/pkg/gen/python-templates/yaml.py.mustache +++ b/pkg/gen/python-templates/yaml.py.mustache @@ -188,14 +188,14 @@ def _parse_yaml_object( gvk = f"{api_version}/{kind}" {{#Groups}} {{#Versions}} -{{#Kinds}} +{{#TopLevelKinds}} if gvk == "{{RawAPIVersion}}/{{Kind}}": # Import locally to avoid name collisions. from pulumi_kubernetes.{{Group}}.{{Version}} import {{Kind}} return [identifier.apply( lambda x: (f"{{RawAPIVersion}}/{{Kind}}:{x}", {{Kind}}(f"{x}", opts, **obj)))] -{{/Kinds}} +{{/TopLevelKinds}} {{/Versions}} {{/Groups}} return [identifier.apply( diff --git a/pkg/gen/python.go b/pkg/gen/python.go index 471dd7258b..d5ed58f447 100644 --- a/pkg/gen/python.go +++ b/pkg/gen/python.go @@ -45,7 +45,7 @@ func PythonClient( // Generate casing tables from property names. // { properties: [ {name: fooBar, casedName: foo_bar}, ]} - properties := allCamelCasePropertyNames(definitions, pythonProvider()) + properties := allCamelCasePropertyNames(definitions, pythonOpts()) cases := map[string][]map[string]string{"properties": make([]map[string]string, 0)} for _, name := range properties { cases["properties"] = append(cases["properties"], @@ -61,7 +61,7 @@ func PythonClient( return err } - groupsSlice := createGroups(definitions, pythonProvider()) + groupsSlice := createGroups(definitions, pythonOpts()) yamlPy, err := mustache.RenderFile( fmt.Sprintf("%s/yaml.py.mustache", templateDir), @@ -90,6 +90,9 @@ func PythonClient( } for _, group := range groupsSlice { + if !group.HasTopLevelKinds() { + continue + } groupInitPy, err := mustache.RenderFile( fmt.Sprintf("%s/group__init__.py.mustache", templateDir), group) @@ -118,6 +121,10 @@ from .CustomResource import (CustomResource) } for _, version := range group.Versions() { + if !version.HasTopLevelKinds() { + continue + } + versionInitPy, err := mustache.RenderFile( fmt.Sprintf("%s/version__init__.py.mustache", templateDir), version) if err != nil { @@ -129,7 +136,7 @@ from .CustomResource import (CustomResource) return err } - for _, kind := range version.Kinds() { + for _, kind := range version.TopLevelKinds() { inputMap := map[string]interface{}{ "RawAPIVersion": kind.RawAPIVersion(), "Comment": kind.Comment(), diff --git a/pkg/gen/typegen.go b/pkg/gen/typegen.go index 5497df3fb0..9814670959 100644 --- a/pkg/gen/typegen.go +++ b/pkg/gen/typegen.go @@ -55,6 +55,8 @@ const ( type GroupConfig struct { group string versions []*VersionConfig + + hasTopLevelKinds bool } // Group returns the name of the group (e.g., `core` for core, etc.) @@ -64,6 +66,9 @@ func (gc *GroupConfig) Group() string { return gc.group } // has `v1beta1`, `v1beta2`, and `v1`. func (gc *GroupConfig) Versions() []*VersionConfig { return gc.versions } +// HasTopLevelKinds returns true if this group has top-level kinds. +func (gc *GroupConfig) HasTopLevelKinds() bool { return gc.hasTopLevelKinds } + // VersionConfig represents a version of a Kubernetes API group (e.g., the `apps` group has // `v1beta1`, `v1beta2`, and `v1`.) type VersionConfig struct { @@ -73,6 +78,8 @@ type VersionConfig struct { gv *schema.GroupVersion // Used for sorting. apiVersion string rawAPIVersion string + + hasTopLevelKinds bool } // Version returns the name of the version (e.g., `apps/v1beta1` would return `v1beta1`). @@ -82,11 +89,25 @@ func (vc *VersionConfig) Version() string { return vc.version } // `apps/v1beta1` has the `Deployment` kind, etc.). func (vc *VersionConfig) Kinds() []*KindConfig { return vc.kinds } -// KindsAndAliases will produce a list of kinds, including aliases (e.g., both `apiregistration` and +// HasTopLevelKinds returns true if this group has top-level kinds. +func (vc *VersionConfig) HasTopLevelKinds() bool { return vc.hasTopLevelKinds } + +// TopLevelKinds returns the set of kinds that are not nested. +func (vc *VersionConfig) TopLevelKinds() []*KindConfig { + var kinds []*KindConfig + for _, k := range vc.kinds { + if !k.IsNested() { + kinds = append(kinds, k) + } + } + return kinds +} + +// TopLevelKindsAndAliases will produce a list of kinds, including aliases (e.g., both `apiregistration` and // `apiregistration.k8s.io`). -func (vc *VersionConfig) KindsAndAliases() []*KindConfig { +func (vc *VersionConfig) TopLevelKindsAndAliases() []*KindConfig { kindsAndAliases := []*KindConfig{} - for _, kind := range vc.kinds { + for _, kind := range vc.TopLevelKinds() { kindsAndAliases = append(kindsAndAliases, kind) if strings.HasPrefix(kind.APIVersion(), apiRegistration) { alias := KindConfig{} @@ -102,12 +123,12 @@ func (vc *VersionConfig) KindsAndAliases() []*KindConfig { return kindsAndAliases } -// ListKindsAndAliases will return all known `Kind`s that are lists, or aliases of lists. These +// ListTopLevelKindsAndAliases will return all known `Kind`s that are lists, or aliases of lists. These // `Kind`s are not instantiated by the API server, and we must "flatten" them client-side to get an // accurate view of what resource operations we need to perform. -func (vc *VersionConfig) ListKindsAndAliases() []*KindConfig { +func (vc *VersionConfig) ListTopLevelKindsAndAliases() []*KindConfig { listKinds := []*KindConfig{} - for _, kind := range vc.KindsAndAliases() { + for _, kind := range vc.TopLevelKindsAndAliases() { hasItems := false for _, prop := range kind.properties { if prop.name == "items" { @@ -146,6 +167,8 @@ type KindConfig struct { apiVersion string rawAPIVersion string typeGuard string + + isNested bool } // Kind returns the name of the Kubernetes API kind (e.g., `Deployment` for @@ -195,19 +218,23 @@ func (kc *KindConfig) URNAPIVersion() string { // TypeGuard returns the text of a TypeScript type guard for the given kind. func (kc *KindConfig) TypeGuard() string { return kc.typeGuard } +// IsNested returns true if this is a nested kind. +func (kc *KindConfig) IsNested() bool { return kc.isNested } + // Property represents a property we want to expose on a Kubernetes API kind (i.e., things that we // will want to `.` into, like `thing.apiVersion`, `thing.kind`, `thing.metadata`, etc.). type Property struct { - name string - languageName string - comment string - pythonConstructorComment string - propType string - pythonConstructorPropType string - defaultValue string - isLast bool - dotnetVarName string - dotnetIsListOrMap bool + name string + languageName string + comment string + pythonConstructorComment string + inputsAPIType string + outputsAPIType string + providerType string + defaultValue string + isLast bool + dotnetVarName string + dotnetIsListOrMap bool } // Name returns the name of the property. @@ -223,12 +250,14 @@ func (p *Property) Comment() string { return p.comment } // constructor documentation. func (p *Property) PythonConstructorComment() string { return p.pythonConstructorComment } -// PropType returns the type of the property. -func (p *Property) PropType() string { return p.propType } +// InputsAPIType returns the type of the property for the inputs API. +func (p *Property) InputsAPIType() string { return p.inputsAPIType } + +// OutputsAPIType returns the type of the property for the outputs API. +func (p *Property) OutputsAPIType() string { return p.outputsAPIType } -// PythonConstructorPropType returns the type of the property, typed for the Python constructor -// resource inputs. -func (p *Property) PythonConstructorPropType() string { return p.pythonConstructorPropType } +// ProviderType returns the type of the property for the provider API. +func (p *Property) ProviderType() string { return p.providerType } // DefaultValue returns the type of the property. func (p *Property) DefaultValue() string { return p.defaultValue } @@ -395,9 +424,9 @@ const ( v1CRSubresourceStatus = apiextensionsV1 + ".CustomResourceSubresourceStatus" ) -func makeTypescriptType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { +func makeTypescriptType(resourceType, propName string, prop map[string]interface{}, gentype gentype) string { wrapType := func(typ string) string { - switch opts.generatorType { + switch gentype { case provider: return fmt.Sprintf("pulumi.Output<%s>", typ) case outputsAPI: @@ -405,12 +434,12 @@ func makeTypescriptType(resourceType, propName string, prop map[string]interface case inputsAPI: return fmt.Sprintf("pulumi.Input<%s>", typ) default: - panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) + panic(fmt.Sprintf("unrecognized generator type %d", gentype)) } } refPrefix := "" - if opts.generatorType == provider { + if gentype == provider { refPrefix = "outputs" } @@ -418,8 +447,8 @@ func makeTypescriptType(resourceType, propName string, prop map[string]interface tstr := t.(string) if tstr == "array" { elemType := makeTypescriptType( - resourceType, propName, prop["items"].(map[string]interface{}), opts) - switch opts.generatorType { + resourceType, propName, prop["items"].(map[string]interface{}), gentype) + switch gentype { case provider: return fmt.Sprintf("%s[]>", elemType[:len(elemType)-1]) case outputsAPI: @@ -435,7 +464,7 @@ func makeTypescriptType(resourceType, propName string, prop map[string]interface if additionalProperties, exists := prop["additionalProperties"]; exists { mapType := additionalProperties.(map[string]interface{}) if ktype, exists := mapType["type"]; exists && len(mapType) == 1 { - switch opts.generatorType { + switch gentype { case inputsAPI: return fmt.Sprintf("pulumi.Input<{[key: %s]: pulumi.Input<%s>}>", ktype, ktype) case outputsAPI: @@ -449,7 +478,7 @@ func makeTypescriptType(resourceType, propName string, prop map[string]interface // Special case: `.metadata.namespace` should either take a string or a namespace object // itself. - switch opts.generatorType { + switch gentype { case inputsAPI: // TODO: Enable metadata to take explicit namespaces, like: // return "pulumi.Input | Namespace" @@ -504,9 +533,9 @@ func makeTypescriptType(resourceType, propName string, prop map[string]interface return wrapType(gvkRefStr) } -func makePythonType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { +func makePythonType(resourceType, propName string, prop map[string]interface{}, gentype gentype) string { wrapType := func(typ string) string { - switch opts.generatorType { + switch gentype { case provider: return fmt.Sprintf("pulumi.Output[%s]", typ) case outputsAPI: @@ -514,7 +543,7 @@ func makePythonType(resourceType, propName string, prop map[string]interface{}, case inputsAPI: return fmt.Sprintf("pulumi.Input[%s]", typ) default: - panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) + panic(fmt.Sprintf("unrecognized generator type %d", gentype)) } } @@ -560,18 +589,18 @@ func makePythonType(resourceType, propName string, prop map[string]interface{}, return wrapType(ref) } -func makeDotnetType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { +func makeDotnetType(resourceType, propName string, prop map[string]interface{}, gentype gentype, forceNoWrap bool) string { refPrefix := "" - if opts.generatorType == provider { + if gentype == provider { refPrefix = "Types.Outputs" } wrapType := func(typ string) string { - if opts.forceNoWrap { + if forceNoWrap { return typ } - switch opts.generatorType { + switch gentype { case provider: return fmt.Sprintf("Output<%s>", typ) case outputsAPI: @@ -579,15 +608,15 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, case inputsAPI: return fmt.Sprintf("Input<%s>", typ) default: - panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) + panic(fmt.Sprintf("unrecognized generator type %d", gentype)) } } oneOf := func(typeA string, typeB string) string { - if opts.forceNoWrap { + if forceNoWrap { return fmt.Sprintf("Union<%s,%s>", typeA, typeB) } - switch opts.generatorType { + switch gentype { case provider: return fmt.Sprintf("Output>", typeA, typeB) case outputsAPI: @@ -595,27 +624,25 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, case inputsAPI: return fmt.Sprintf("InputUnion<%s,%s>", typeA, typeB) default: - panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) + panic(fmt.Sprintf("unrecognized generator type %d", gentype)) } } if t, exists := prop["type"]; exists { tstr := t.(string) if tstr == "array" { - switch opts.generatorType { + switch gentype { case provider: elemType := makeDotnetType( - resourceType, propName, prop["items"].(map[string]interface{}), opts) + resourceType, propName, prop["items"].(map[string]interface{}), gentype, forceNoWrap) return fmt.Sprintf("%s[]>", elemType[:len(elemType)-1]) case outputsAPI: elemType := makeDotnetType( - resourceType, propName, prop["items"].(map[string]interface{}), opts) + resourceType, propName, prop["items"].(map[string]interface{}), gentype, forceNoWrap) return fmt.Sprintf("ImmutableArray<%s>", elemType) case inputsAPI: - elemOpts := opts - elemOpts.forceNoWrap = true elemType := makeDotnetType( - resourceType, propName, prop["items"].(map[string]interface{}), elemOpts) + resourceType, propName, prop["items"].(map[string]interface{}), gentype, true) return fmt.Sprintf("InputList<%s>", elemType) } } else if tstr == "integer" { @@ -627,23 +654,19 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, } else if tstr == "object" { vtype := "string" if additionalProperties, exists := prop["additionalProperties"]; exists { - switch opts.generatorType { + switch gentype { case provider: - elemOpts := opts - elemOpts.forceNoWrap = true vtype = makeDotnetType( - resourceType, propName, additionalProperties.(map[string]interface{}), elemOpts) + resourceType, propName, additionalProperties.(map[string]interface{}), gentype, true) case outputsAPI: vtype = makeDotnetType( - resourceType, propName, additionalProperties.(map[string]interface{}), opts) + resourceType, propName, additionalProperties.(map[string]interface{}), gentype, forceNoWrap) case inputsAPI: - elemOpts := opts - elemOpts.forceNoWrap = true vtype = makeDotnetType( - resourceType, propName, additionalProperties.(map[string]interface{}), elemOpts) + resourceType, propName, additionalProperties.(map[string]interface{}), gentype, true) } } - switch opts.generatorType { + switch gentype { case inputsAPI: return fmt.Sprintf("InputMap<%s>", vtype) case outputsAPI: @@ -655,7 +678,7 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, // Special case: `.metadata.namespace` should either take a string or a namespace object // itself. - switch opts.generatorType { + switch gentype { case inputsAPI: // TODO: Enable metadata to take explicit namespaces, like: // return "pulumi.Input | Namespace" @@ -672,7 +695,7 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, var argsSuffix string var stringArr string var jsonType string - switch opts.generatorType { + switch gentype { case inputsAPI: argsSuffix = "Args" stringArr = "InputList" @@ -686,7 +709,7 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, stringArr = "string[]" jsonType = "Output" default: - panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) + panic(fmt.Sprintf("unrecognized generator type %d", gentype)) } isSimpleRef := true @@ -721,7 +744,7 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, group := pascalCase(gvk.Group) version := pascalCase(gvk.Version) kind := gvk.Kind - if opts.generatorType == inputsAPI { + if gentype == inputsAPI { kind = kind + "Args" } @@ -735,16 +758,23 @@ func makeDotnetType(resourceType, propName string, prop map[string]interface{}, return wrapType(gvkRefStr) } -func makeType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { - switch opts.language { +func makeTypes(resourceType, propName string, prop map[string]interface{}, language language) (string, string, string) { + inputsAPIType := makeType(resourceType, propName, prop, language, inputsAPI) + outputsAPIType := makeType(resourceType, propName, prop, language, outputsAPI) + providerType := makeType(resourceType, propName, prop, language, provider) + return inputsAPIType, outputsAPIType, providerType +} + +func makeType(resourceType, propName string, prop map[string]interface{}, language language, gentype gentype) string { + switch language { case typescript: - return makeTypescriptType(resourceType, propName, prop, opts) + return makeTypescriptType(resourceType, propName, prop, gentype) case python: - return makePythonType(resourceType, propName, prop, opts) + return makePythonType(resourceType, propName, prop, gentype) case dotnet: - return makeDotnetType(resourceType, propName, prop, opts) + return makeDotnetType(resourceType, propName, prop, gentype, false) default: - panic(fmt.Sprintf("Unsupported language '%s'", opts.language)) + panic(fmt.Sprintf("Unsupported language '%s'", language)) } } @@ -812,20 +842,12 @@ const ( ) type groupOpts struct { - generatorType gentype - language language - forceNoWrap bool + language language } -func nodeJSInputs() groupOpts { return groupOpts{generatorType: inputsAPI, language: typescript} } -func nodeJSOutputs() groupOpts { return groupOpts{generatorType: outputsAPI, language: typescript} } -func nodeJSProvider() groupOpts { return groupOpts{generatorType: provider, language: typescript} } - -func pythonProvider() groupOpts { return groupOpts{generatorType: provider, language: python} } - -func dotnetInputs() groupOpts { return groupOpts{generatorType: inputsAPI, language: dotnet} } -func dotnetOutputs() groupOpts { return groupOpts{generatorType: outputsAPI, language: dotnet} } -func dotnetProvider() groupOpts { return groupOpts{generatorType: provider, language: dotnet} } +func nodeJSOpts() groupOpts { return groupOpts{language: typescript} } +func pythonOpts() groupOpts { return groupOpts{language: python} } +func dotnetOpts() groupOpts { return groupOpts{language: dotnet} } func allCamelCasePropertyNames(definitionsJSON map[string]interface{}, opts groupOpts) []string { // Map definition JSON object -> `definition` with metadata. @@ -941,21 +963,19 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro prop := d.data["properties"].(map[string]interface{})[propName].(map[string]interface{}) var prefix string - var t, pyConstructorT string + var inputsAPIType, outputsAPIType, providerType string isListOrMap := false switch opts.language { case typescript: prefix = " " - t = makeType(d.name, propName, prop, opts) + inputsAPIType, outputsAPIType, providerType = makeTypes(d.name, propName, prop, typescript) case python: prefix = " " - t = makeType(d.name, propName, prop, opts) - pyConstructorT = makeType(d.name, propName, prop, - groupOpts{language: python, generatorType: inputsAPI}) + inputsAPIType, outputsAPIType, providerType = makeTypes(d.name, propName, prop, python) case dotnet: prefix = " " - t = makeType(d.name, propName, prop, opts) - if strings.HasPrefix(t, "InputList") || strings.HasPrefix(t, "InputMap") { + inputsAPIType, outputsAPIType, providerType = makeTypes(d.name, propName, prop, dotnet) + if strings.HasPrefix(inputsAPIType, "InputList") || strings.HasPrefix(inputsAPIType, "InputMap") { isListOrMap = true } default: @@ -971,26 +991,16 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro case "apiVersion": defaultValue = fmt.Sprintf(`"%s"`, defaultGroupVersion) if opts.language == typescript && isTopLevel { - switch opts.generatorType { - case provider: - t = fmt.Sprintf(`pulumi.Output<"%s">`, defaultGroupVersion) - case outputsAPI: - t = fmt.Sprintf(`"%s"`, defaultGroupVersion) - case inputsAPI: - t = fmt.Sprintf(`pulumi.Input<"%s">`, defaultGroupVersion) - } + inputsAPIType = fmt.Sprintf(`pulumi.Input<"%s">`, defaultGroupVersion) + outputsAPIType = fmt.Sprintf(`"%s"`, defaultGroupVersion) + providerType = fmt.Sprintf(`pulumi.Output<"%s">`, defaultGroupVersion) } case "kind": defaultValue = fmt.Sprintf(`"%s"`, d.gvk.Kind) if opts.language == typescript && isTopLevel { - switch opts.generatorType { - case provider: - t = fmt.Sprintf(`pulumi.Output<"%s">`, d.gvk.Kind) - case outputsAPI: - t = fmt.Sprintf(`"%s"`, d.gvk.Kind) - case inputsAPI: - t = fmt.Sprintf(`pulumi.Input<"%s">`, d.gvk.Kind) - } + inputsAPIType = fmt.Sprintf(`pulumi.Input<"%s">`, d.gvk.Kind) + outputsAPIType = fmt.Sprintf(`"%s"`, d.gvk.Kind) + providerType = fmt.Sprintf(`pulumi.Output<"%s">`, d.gvk.Kind) } } @@ -1019,16 +1029,17 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro } return &Property{ - comment: fmtComment(prop["description"], prefix, false, opts, d.gvk), - pythonConstructorComment: fmtComment(prop["description"], prefix+prefix+" ", true, opts, d.gvk), - propType: t, - pythonConstructorPropType: pyConstructorT, - name: propName, - languageName: languageName, - dotnetVarName: dotnetVarName, - defaultValue: defaultValue, - isLast: false, - dotnetIsListOrMap: isListOrMap, + comment: fmtComment(prop["description"], prefix, false, opts, d.gvk), + pythonConstructorComment: fmtComment(prop["description"], prefix+prefix+" ", true, opts, d.gvk), + inputsAPIType: inputsAPIType, + outputsAPIType: outputsAPIType, + providerType: providerType, + name: propName, + languageName: languageName, + dotnetVarName: dotnetVarName, + defaultValue: defaultValue, + isLast: false, + dotnetIsListOrMap: isListOrMap, } }) @@ -1065,10 +1076,6 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro return linq.From([]*KindConfig{}) } - if opts.generatorType == provider && (!isTopLevel) { - return linq.From([]*KindConfig{}) - } - var typeGuard string props := d.data["properties"].(map[string]interface{}) _, apiVersionExists := props["apiVersion"] @@ -1095,6 +1102,7 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro apiVersion: fqGroupVersion, rawAPIVersion: defaultGroupVersion, typeGuard: typeGuard, + isNested: !isTopLevel, }, }) }). @@ -1125,13 +1133,18 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro version = pascalCase(version) } + hasTopLevelKinds := linq.From(kindsGroup).WhereT(func(k *KindConfig) bool { + return !k.IsNested() + }).Any() + return linq.From([]*VersionConfig{ { - version: version, - kinds: kindsGroup, - gv: &gv, - apiVersion: kindsGroup[0].apiVersion, // NOTE: This is safe. - rawAPIVersion: kindsGroup[0].rawAPIVersion, // NOTE: This is safe. + version: version, + kinds: kindsGroup, + gv: &gv, + apiVersion: kindsGroup[0].apiVersion, // NOTE: This is safe. + rawAPIVersion: kindsGroup[0].rawAPIVersion, // NOTE: This is safe. + hasTopLevelKinds: hasTopLevelKinds, }, }) }). @@ -1159,10 +1172,15 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro group = pascalCase(group) } + hasTopLevelKinds := linq.From(versionsGroup).WhereT(func(v *VersionConfig) bool { + return v.HasTopLevelKinds() + }).Any() + return linq.From([]*GroupConfig{ { - group: group, - versions: versionsGroup, + group: group, + versions: versionsGroup, + hasTopLevelKinds: hasTopLevelKinds, }, }) }).