2021-08-23 20:46:09 +00:00
|
|
|
// Copyright 2016-2021, Pulumi Corporation.
|
2020-01-21 22:45:48 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
// Pulling out some of the repeated strings tokens into constants would harm readability, so we just ignore the
|
|
|
|
// goconst linter's warning.
|
|
|
|
//
|
2023-01-06 00:07:45 +00:00
|
|
|
//nolint:lll, goconst
|
2020-01-21 22:45:48 +00:00
|
|
|
package gen
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
_ "embed"
|
2020-01-21 22:45:48 +00:00
|
|
|
"fmt"
|
2020-03-19 15:32:40 +00:00
|
|
|
"go/format"
|
2020-01-21 22:45:48 +00:00
|
|
|
"io"
|
2021-01-04 21:22:56 +00:00
|
|
|
"os"
|
2020-01-21 22:45:48 +00:00
|
|
|
"path"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2022-08-12 18:04:21 +00:00
|
|
|
"sync"
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
2022-10-17 16:37:07 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/cgstrings"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
|
2021-09-08 05:23:30 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
|
2021-12-06 21:10:30 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
|
2023-06-28 16:02:04 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/slice"
|
2021-09-08 05:23:30 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-01-21 22:45:48 +00:00
|
|
|
)
|
|
|
|
|
2022-11-22 20:44:19 +00:00
|
|
|
// A signifier that the module is external, and will never match.
|
|
|
|
//
|
|
|
|
// This token is always an invalid module since ':' is not allowed within modules.
|
|
|
|
const ExternalModuleSig = ":always-external:"
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
const (
|
|
|
|
GenericsSettingNone = "none"
|
|
|
|
GenericsSettingSideBySide = "side-by-side"
|
|
|
|
GenericsSettingGenericsOnly = "generics-only"
|
|
|
|
)
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
type typeDetails struct {
|
2021-12-10 23:35:24 +00:00
|
|
|
// Note: if any of {ptr,array,map}Input are set, input and the corresponding output field must also be set. The
|
|
|
|
// mark* functions ensure that these invariants hold.
|
|
|
|
input bool
|
|
|
|
ptrInput bool
|
|
|
|
arrayInput bool
|
|
|
|
mapInput bool
|
|
|
|
|
|
|
|
// Note: if any of {ptr,array,map}Output are set, output must also be set. The mark* functions ensure that these
|
|
|
|
// invariants hold.
|
|
|
|
output bool
|
|
|
|
ptrOutput bool
|
|
|
|
arrayOutput bool
|
|
|
|
mapOutput bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *typeDetails) hasOutputs() bool {
|
|
|
|
return d.output || d.ptrOutput || d.arrayOutput || d.mapOutput
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *typeDetails) mark(input, output bool) {
|
|
|
|
d.input = d.input || input
|
|
|
|
d.output = d.output || input || output
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *typeDetails) markPtr(input, output bool) {
|
|
|
|
d.mark(input, output)
|
|
|
|
d.ptrInput = d.ptrInput || input
|
|
|
|
d.ptrOutput = d.ptrOutput || input || output
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *typeDetails) markArray(input, output bool) {
|
|
|
|
d.mark(input, output)
|
|
|
|
d.arrayInput = d.arrayInput || input
|
|
|
|
d.arrayOutput = d.arrayOutput || input || output
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *typeDetails) markMap(input, output bool) {
|
|
|
|
d.mark(input, output)
|
|
|
|
d.mapInput = d.mapInput || input
|
|
|
|
d.mapOutput = d.mapOutput || input || output
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 00:01:33 +00:00
|
|
|
// Title converts the input string to a title case
|
|
|
|
// where only the initial letter is upper-cased.
|
2020-05-01 10:45:47 +00:00
|
|
|
// It also removes $-prefix if any.
|
2020-04-07 00:01:33 +00:00
|
|
|
func Title(s string) string {
|
2020-01-21 22:45:48 +00:00
|
|
|
if s == "" {
|
|
|
|
return ""
|
|
|
|
}
|
2020-05-01 10:45:47 +00:00
|
|
|
if s[0] == '$' {
|
|
|
|
return Title(s[1:])
|
|
|
|
}
|
2022-10-17 16:37:07 +00:00
|
|
|
s = cgstrings.UppercaseFirst(s)
|
|
|
|
s = cgstrings.Unhyphenate(s)
|
|
|
|
return s
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
func tokenToPackage(pkg schema.PackageReference, overrides map[string]string, tok string) string {
|
2020-07-14 17:58:29 +00:00
|
|
|
mod := pkg.TokenToModule(tok)
|
|
|
|
if override, ok := overrides[mod]; ok {
|
|
|
|
mod = override
|
|
|
|
}
|
|
|
|
return strings.ToLower(mod)
|
|
|
|
}
|
|
|
|
|
2022-08-12 18:04:21 +00:00
|
|
|
// A threadsafe cache for sharing between invocations of GenerateProgram.
|
|
|
|
type Cache struct {
|
|
|
|
externalPackages map[*schema.Package]map[string]*pkgContext
|
|
|
|
m *sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
var globalCache = NewCache()
|
|
|
|
|
|
|
|
func NewCache() *Cache {
|
|
|
|
return &Cache{
|
|
|
|
externalPackages: map[*schema.Package]map[string]*pkgContext{},
|
|
|
|
m: new(sync.Mutex),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cache) lookupContextMap(pkg *schema.Package) (map[string]*pkgContext, bool) {
|
|
|
|
c.m.Lock()
|
|
|
|
defer c.m.Unlock()
|
|
|
|
m, ok := c.externalPackages[pkg]
|
|
|
|
return m, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cache) setContextMap(pkg *schema.Package, m map[string]*pkgContext) {
|
|
|
|
c.m.Lock()
|
|
|
|
defer c.m.Unlock()
|
|
|
|
c.externalPackages[pkg] = m
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
type pkgContext struct {
|
2022-12-08 10:45:46 +00:00
|
|
|
pkg schema.PackageReference
|
2021-04-29 19:30:01 +00:00
|
|
|
mod string
|
|
|
|
importBasePath string
|
|
|
|
rootPackageName string
|
|
|
|
typeDetails map[schema.Type]*typeDetails
|
|
|
|
enums []*schema.EnumType
|
|
|
|
types []*schema.ObjectType
|
|
|
|
resources []*schema.Resource
|
|
|
|
functions []*schema.Function
|
2021-09-28 14:33:14 +00:00
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
// schemaNames tracks the names of types/resources as specified in the schema
|
2021-09-04 02:42:45 +00:00
|
|
|
schemaNames codegen.StringSet
|
|
|
|
names codegen.StringSet
|
|
|
|
renamed map[string]string
|
2021-09-28 14:33:14 +00:00
|
|
|
|
2022-07-06 18:35:31 +00:00
|
|
|
// A mapping between external packages and their bound contents.
|
2022-08-12 18:04:21 +00:00
|
|
|
externalPackages *Cache
|
2022-07-06 18:35:31 +00:00
|
|
|
|
2021-09-04 02:42:45 +00:00
|
|
|
// duplicateTokens tracks tokens that exist for both types and resources
|
|
|
|
duplicateTokens map[string]bool
|
|
|
|
functionNames map[*schema.Function]string
|
|
|
|
tool string
|
|
|
|
packages map[string]*pkgContext
|
2020-03-17 20:09:43 +00:00
|
|
|
|
2020-04-20 23:36:05 +00:00
|
|
|
// Name overrides set in GoPackageInfo
|
2020-03-17 20:09:43 +00:00
|
|
|
modToPkg map[string]string // Module name -> package name
|
|
|
|
pkgImportAliases map[string]string // Package name -> import alias
|
2023-08-22 17:16:43 +00:00
|
|
|
// the name used for the internal module, defaults to "internal" if not set by the schema
|
|
|
|
internalModuleName string
|
2021-10-01 18:33:02 +00:00
|
|
|
|
|
|
|
// Determines whether to make single-return-value methods return an output struct or the value
|
|
|
|
liftSingleValueMethodReturns bool
|
2021-10-12 17:15:24 +00:00
|
|
|
|
|
|
|
// Determines if we should emit type registration code
|
|
|
|
disableInputTypeRegistrations bool
|
2021-11-23 23:10:15 +00:00
|
|
|
|
|
|
|
// Determines if we should emit object defaults code
|
|
|
|
disableObjectDefaults bool
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
func (pkg *pkgContext) detailsForType(t schema.Type) *typeDetails {
|
2021-06-24 16:17:55 +00:00
|
|
|
if obj, ok := t.(*schema.ObjectType); ok && obj.IsInputShape() {
|
|
|
|
t = obj.PlainShape
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
details, ok := pkg.typeDetails[t]
|
|
|
|
if !ok {
|
|
|
|
details = &typeDetails{}
|
|
|
|
pkg.typeDetails[t] = details
|
|
|
|
}
|
|
|
|
return details
|
|
|
|
}
|
|
|
|
|
2020-07-14 17:58:29 +00:00
|
|
|
func (pkg *pkgContext) tokenToPackage(tok string) string {
|
|
|
|
return tokenToPackage(pkg.pkg, pkg.modToPkg, tok)
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
func (pkg *pkgContext) tokenToType(tok string) string {
|
|
|
|
// token := pkg : module : member
|
|
|
|
// module := path/to/module
|
|
|
|
|
|
|
|
components := strings.Split(tok, ":")
|
2021-03-15 21:44:11 +00:00
|
|
|
contract.Assertf(len(components) == 3, "tok: %s", tok)
|
2022-12-12 19:14:02 +00:00
|
|
|
contract.Assertf(pkg != nil, "pkg is nil. token %s", tok)
|
|
|
|
contract.Assertf(pkg.pkg != nil, "pkg.pkg is nil. token %s", tok)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2020-07-14 17:58:29 +00:00
|
|
|
mod, name := pkg.tokenToPackage(tok), components[2]
|
2020-03-17 20:09:43 +00:00
|
|
|
|
2020-04-07 00:01:33 +00:00
|
|
|
name = Title(name)
|
2021-09-23 22:42:48 +00:00
|
|
|
if modPkg, ok := pkg.packages[mod]; ok {
|
2021-01-27 07:16:42 +00:00
|
|
|
newName, renamed := modPkg.renamed[name]
|
|
|
|
if renamed {
|
|
|
|
name = newName
|
2021-09-22 03:48:45 +00:00
|
|
|
} else if modPkg.duplicateTokens[strings.ToLower(tok)] {
|
2021-09-04 02:42:45 +00:00
|
|
|
// maintain support for duplicate tokens for types and resources in Kubernetes
|
2021-09-23 22:42:48 +00:00
|
|
|
name += "Type"
|
2021-01-04 21:22:56 +00:00
|
|
|
}
|
2020-03-19 15:32:40 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
if mod == pkg.mod {
|
|
|
|
return name
|
|
|
|
}
|
2020-07-10 18:23:31 +00:00
|
|
|
if mod == "" {
|
2022-12-08 10:45:46 +00:00
|
|
|
var err error
|
|
|
|
mod, err = packageRoot(pkg.pkg)
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.AssertNoErrorf(err, "Unable to determine package root")
|
2020-07-10 18:23:31 +00:00
|
|
|
}
|
2022-02-03 15:43:05 +00:00
|
|
|
|
|
|
|
var importPath string
|
|
|
|
if alias, hasAlias := pkg.pkgImportAliases[path.Join(pkg.importBasePath, mod)]; hasAlias {
|
|
|
|
importPath = alias
|
|
|
|
} else {
|
2022-12-12 19:14:02 +00:00
|
|
|
importPath = mod[strings.IndexRune(mod, '/')+1:]
|
2022-02-03 15:43:05 +00:00
|
|
|
importPath = strings.ReplaceAll(importPath, "-", "")
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.ReplaceAll(importPath+"."+name, "-provider", "")
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2022-06-22 15:57:05 +00:00
|
|
|
// Resolve a enum type to its name.
|
|
|
|
func (pkg *pkgContext) resolveEnumType(t *schema.EnumType) string {
|
|
|
|
if !pkg.isExternalReference(t) {
|
2022-07-06 18:35:31 +00:00
|
|
|
return pkg.tokenToEnum(t.Token)
|
2022-06-22 15:57:05 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 18:35:31 +00:00
|
|
|
extPkgCtx, _ := pkg.contextForExternalReference(t)
|
2022-11-22 20:44:19 +00:00
|
|
|
enumType := extPkgCtx.typeString(t)
|
2022-06-22 15:57:05 +00:00
|
|
|
return enumType
|
|
|
|
}
|
|
|
|
|
2021-09-23 22:42:48 +00:00
|
|
|
func (pkg *pkgContext) tokenToEnum(tok string) string {
|
|
|
|
// token := pkg : module : member
|
|
|
|
// module := path/to/module
|
|
|
|
|
|
|
|
components := strings.Split(tok, ":")
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(len(components) == 3, "Token must have 3 components, got %d", len(components))
|
2021-09-23 22:42:48 +00:00
|
|
|
if pkg == nil {
|
|
|
|
panic(fmt.Errorf("pkg is nil. token %s", tok))
|
|
|
|
}
|
|
|
|
if pkg.pkg == nil {
|
|
|
|
panic(fmt.Errorf("pkg.pkg is nil. token %s", tok))
|
|
|
|
}
|
|
|
|
|
|
|
|
mod, name := pkg.tokenToPackage(tok), components[2]
|
|
|
|
|
|
|
|
name = Title(name)
|
2022-06-22 15:57:05 +00:00
|
|
|
|
2021-09-23 22:42:48 +00:00
|
|
|
if modPkg, ok := pkg.packages[mod]; ok {
|
|
|
|
newName, renamed := modPkg.renamed[name]
|
|
|
|
if renamed {
|
|
|
|
name = newName
|
|
|
|
} else if modPkg.duplicateTokens[tok] {
|
|
|
|
// If the package containing the enum's token already has a resource or type with the
|
|
|
|
// same name, add an `Enum` suffix.
|
|
|
|
name += "Enum"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mod == pkg.mod {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
if mod == "" {
|
|
|
|
mod = components[0]
|
|
|
|
}
|
2022-06-22 15:57:05 +00:00
|
|
|
|
|
|
|
var importPath string
|
|
|
|
if alias, hasAlias := pkg.pkgImportAliases[path.Join(pkg.importBasePath, mod)]; hasAlias {
|
|
|
|
importPath = alias
|
|
|
|
} else {
|
|
|
|
importPath = strings.ReplaceAll(mod, "/", "")
|
|
|
|
}
|
|
|
|
|
|
|
|
return importPath + "." + name
|
2021-09-23 22:42:48 +00:00
|
|
|
}
|
|
|
|
|
2020-11-09 18:55:53 +00:00
|
|
|
func (pkg *pkgContext) tokenToResource(tok string) string {
|
|
|
|
// token := pkg : module : member
|
|
|
|
// module := path/to/module
|
|
|
|
|
|
|
|
components := strings.Split(tok, ":")
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(len(components) == 3, "Token must have 3 components, got %d", len(components))
|
2020-11-09 18:55:53 +00:00
|
|
|
if pkg == nil {
|
|
|
|
panic(fmt.Errorf("pkg is nil. token %s", tok))
|
|
|
|
}
|
|
|
|
if pkg.pkg == nil {
|
|
|
|
panic(fmt.Errorf("pkg.pkg is nil. token %s", tok))
|
|
|
|
}
|
|
|
|
|
2021-01-15 18:06:57 +00:00
|
|
|
// Is it a provider resource?
|
|
|
|
if components[0] == "pulumi" && components[1] == "providers" {
|
2023-12-12 12:19:42 +00:00
|
|
|
return components[2] + ".Provider"
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
|
|
|
|
2020-11-09 18:55:53 +00:00
|
|
|
mod, name := pkg.tokenToPackage(tok), components[2]
|
|
|
|
|
|
|
|
name = Title(name)
|
|
|
|
|
|
|
|
if mod == pkg.mod {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
if mod == "" {
|
|
|
|
mod = components[0]
|
|
|
|
}
|
2022-02-03 15:43:05 +00:00
|
|
|
|
|
|
|
var importPath string
|
|
|
|
if alias, hasAlias := pkg.pkgImportAliases[path.Join(pkg.importBasePath, mod)]; hasAlias {
|
|
|
|
importPath = alias
|
|
|
|
} else {
|
|
|
|
importPath = strings.ReplaceAll(mod, "/", "")
|
|
|
|
}
|
|
|
|
|
|
|
|
return importPath + "." + name
|
2020-11-09 18:55:53 +00:00
|
|
|
}
|
|
|
|
|
2021-01-19 23:59:51 +00:00
|
|
|
func tokenToModule(tok string) string {
|
|
|
|
// token := pkg : module : member
|
|
|
|
// module := path/to/module
|
|
|
|
|
|
|
|
components := strings.Split(tok, ":")
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(len(components) == 3, "Token must have 3 components, got %d", len(components))
|
2021-01-19 23:59:51 +00:00
|
|
|
return components[1]
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
func tokenToName(tok string) string {
|
|
|
|
components := strings.Split(tok, ":")
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(len(components) == 3, "Token must have 3 components, got %d", len(components))
|
2020-04-07 00:01:33 +00:00
|
|
|
return Title(components[2])
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-09-04 02:42:45 +00:00
|
|
|
// disambiguatedResourceName gets the name of a resource as it should appear in source, resolving conflicts in the process.
|
|
|
|
func disambiguatedResourceName(r *schema.Resource, pkg *pkgContext) string {
|
|
|
|
name := rawResourceName(r)
|
|
|
|
if renamed, ok := pkg.renamed[name]; ok {
|
|
|
|
name = renamed
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
// rawResourceName produces raw resource name translated from schema type token without resolving conflicts or dupes.
|
|
|
|
func rawResourceName(r *schema.Resource) string {
|
2020-01-21 22:45:48 +00:00
|
|
|
if r.IsProvider {
|
|
|
|
return "Provider"
|
|
|
|
}
|
|
|
|
return tokenToName(r.Token)
|
|
|
|
}
|
|
|
|
|
2021-11-23 23:10:15 +00:00
|
|
|
// If `nil` is a valid value of type `t`.
|
2021-06-24 16:17:55 +00:00
|
|
|
func isNilType(t schema.Type) bool {
|
2020-01-21 22:45:48 +00:00
|
|
|
switch t := t.(type) {
|
2021-07-07 23:25:26 +00:00
|
|
|
case *schema.OptionalType, *schema.ArrayType, *schema.MapType, *schema.ResourceType, *schema.InputType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return true
|
|
|
|
case *schema.TokenType:
|
|
|
|
// Use the underlying type for now.
|
|
|
|
if t.UnderlyingType != nil {
|
|
|
|
return isNilType(t.UnderlyingType)
|
|
|
|
}
|
|
|
|
case *schema.UnionType:
|
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
|
|
|
// type for the enum instead
|
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
|
|
|
return isNilType(typ.ElementType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
switch t {
|
|
|
|
case schema.ArchiveType, schema.AssetType, schema.JSONType, schema.AnyType:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) inputType(t schema.Type) (result string) {
|
|
|
|
switch t := codegen.SimplifyInputUnion(t).(type) {
|
|
|
|
case *schema.OptionalType:
|
|
|
|
return pkg.typeString(t)
|
|
|
|
case *schema.InputType:
|
|
|
|
return pkg.inputType(t.ElementType)
|
2020-11-03 07:02:56 +00:00
|
|
|
case *schema.EnumType:
|
2021-06-24 16:17:55 +00:00
|
|
|
// Since enum type is itself an input
|
2022-06-22 15:57:05 +00:00
|
|
|
return pkg.resolveEnumType(t) + "Input"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ArrayType:
|
2021-06-24 16:17:55 +00:00
|
|
|
en := pkg.inputType(t.ElementType)
|
|
|
|
return strings.TrimSuffix(en, "Input") + "ArrayInput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.MapType:
|
2021-06-24 16:17:55 +00:00
|
|
|
en := pkg.inputType(t.ElementType)
|
|
|
|
return strings.TrimSuffix(en, "Input") + "MapInput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ObjectType:
|
2021-06-24 16:17:55 +00:00
|
|
|
if t.IsInputShape() {
|
|
|
|
t = t.PlainShape
|
|
|
|
}
|
|
|
|
return pkg.resolveObjectType(t) + "Input"
|
2020-11-09 18:55:53 +00:00
|
|
|
case *schema.ResourceType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.resolveResourceType(t) + "Input"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.TokenType:
|
|
|
|
// Use the underlying type for now.
|
|
|
|
if t.UnderlyingType != nil {
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.inputType(t.UnderlyingType)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.tokenToType(t.Token) + "Input"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.UnionType:
|
2020-11-03 07:02:56 +00:00
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
2021-06-24 16:17:55 +00:00
|
|
|
// type for the input instead
|
2020-11-03 07:02:56 +00:00
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.inputType(typ.ElementType)
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
// TODO(pdg): union types
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.Input"
|
2020-01-21 22:45:48 +00:00
|
|
|
default:
|
|
|
|
switch t {
|
|
|
|
case schema.BoolType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.BoolInput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.IntType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.IntInput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.NumberType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.Float64Input"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.StringType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.StringInput"
|
|
|
|
case schema.ArchiveType:
|
|
|
|
return "pulumi.ArchiveInput"
|
|
|
|
case schema.AssetType:
|
|
|
|
return "pulumi.AssetOrArchiveInput"
|
|
|
|
case schema.JSONType:
|
|
|
|
fallthrough
|
|
|
|
case schema.AnyType:
|
|
|
|
return "pulumi.Input"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
panic(fmt.Errorf("unexpected type %T", t))
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genericInputTypeImpl(t schema.Type) string {
|
|
|
|
switch t := codegen.SimplifyInputUnion(t).(type) {
|
|
|
|
case *schema.OptionalType:
|
|
|
|
return pkg.genericInputTypeImpl(t.ElementType)
|
|
|
|
case *schema.InputType:
|
|
|
|
return pkg.genericInputTypeImpl(t.ElementType)
|
|
|
|
case *schema.EnumType:
|
|
|
|
return pkg.resolveEnumType(t)
|
|
|
|
case *schema.ArrayType:
|
|
|
|
elementType := pkg.genericInputTypeImpl(t.ElementType)
|
2023-12-12 12:19:42 +00:00
|
|
|
return "[]" + elementType
|
2023-09-19 10:28:50 +00:00
|
|
|
case *schema.MapType:
|
|
|
|
elementType := pkg.genericInputTypeImpl(t.ElementType)
|
2023-12-12 12:19:42 +00:00
|
|
|
return "map[string]" + elementType
|
2023-09-19 10:28:50 +00:00
|
|
|
case *schema.ObjectType:
|
|
|
|
elementType := pkg.resolveObjectType(t)
|
2023-12-12 12:19:42 +00:00
|
|
|
return "*" + elementType
|
2023-09-19 10:28:50 +00:00
|
|
|
case *schema.UnionType:
|
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
|
|
|
// type for the input instead
|
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
|
|
|
return pkg.genericInputTypeImpl(typ.ElementType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "any"
|
|
|
|
default:
|
|
|
|
elementType, _ := pkg.genericElementType(t)
|
|
|
|
return elementType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isArrayType(t schema.Type) bool {
|
|
|
|
switch t.(type) {
|
|
|
|
case *schema.ArrayType:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isMapType(t schema.Type) bool {
|
|
|
|
switch t.(type) {
|
|
|
|
case *schema.MapType:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isOptionalType(t schema.Type) bool {
|
|
|
|
switch t.(type) {
|
|
|
|
case *schema.OptionalType:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func reduceInputType(t schema.Type) schema.Type {
|
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.InputType:
|
|
|
|
return reduceInputType(t.ElementType)
|
|
|
|
default:
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-21 16:43:51 +00:00
|
|
|
func (pkg *pkgContext) genericTypeNeedsPointer(t schema.Type) bool {
|
|
|
|
return isOptionalType(reduceInputType(t)) && !isArrayType(codegen.UnwrapType(t)) && !isMapType(codegen.UnwrapType(t))
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genericInputType(t schema.Type) string {
|
|
|
|
optionalPointer := ""
|
2023-11-21 16:43:51 +00:00
|
|
|
if pkg.genericTypeNeedsPointer(t) {
|
2023-09-19 10:28:50 +00:00
|
|
|
optionalPointer = "*"
|
|
|
|
}
|
|
|
|
|
|
|
|
inputType := pkg.genericInputTypeImpl(t)
|
|
|
|
if strings.HasPrefix(inputType, "*") {
|
|
|
|
optionalPointer = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("pulumix.Input[%s%s]", optionalPointer, inputType)
|
|
|
|
}
|
|
|
|
|
2023-11-21 16:43:51 +00:00
|
|
|
func (pkg *pkgContext) plainGenericInputType(t schema.Type) string {
|
|
|
|
optionalPointer := ""
|
|
|
|
if pkg.genericTypeNeedsPointer(t) {
|
|
|
|
optionalPointer = "*"
|
|
|
|
}
|
|
|
|
|
|
|
|
inputType := pkg.genericInputTypeImpl(t)
|
|
|
|
if strings.HasPrefix(inputType, "*") {
|
|
|
|
optionalPointer = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s%s", optionalPointer, inputType)
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
func (pkg *pkgContext) argsTypeImpl(t schema.Type) (result string) {
|
|
|
|
switch t := codegen.SimplifyInputUnion(t).(type) {
|
|
|
|
case *schema.OptionalType:
|
|
|
|
return pkg.typeStringImpl(t, true)
|
|
|
|
case *schema.InputType:
|
|
|
|
return pkg.argsTypeImpl(t.ElementType)
|
|
|
|
case *schema.EnumType:
|
|
|
|
// Since enum type is itself an input
|
2022-06-22 15:57:05 +00:00
|
|
|
return pkg.resolveEnumType(t)
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.ArrayType:
|
|
|
|
en := pkg.argsTypeImpl(t.ElementType)
|
2023-10-20 16:12:25 +00:00
|
|
|
if en == "pulumi.Any" {
|
|
|
|
en = strings.TrimSuffix(en, "Any")
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return strings.TrimSuffix(en, "Args") + "Array"
|
|
|
|
case *schema.MapType:
|
|
|
|
en := pkg.argsTypeImpl(t.ElementType)
|
2023-10-20 16:12:25 +00:00
|
|
|
if en == "pulumi.Any" {
|
|
|
|
en = strings.TrimSuffix(en, "Any")
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return strings.TrimSuffix(en, "Args") + "Map"
|
|
|
|
case *schema.ObjectType:
|
|
|
|
return pkg.resolveObjectType(t)
|
|
|
|
case *schema.ResourceType:
|
|
|
|
return pkg.resolveResourceType(t)
|
|
|
|
case *schema.TokenType:
|
|
|
|
// Use the underlying type for now.
|
|
|
|
if t.UnderlyingType != nil {
|
|
|
|
return pkg.argsTypeImpl(t.UnderlyingType)
|
|
|
|
}
|
|
|
|
return pkg.tokenToType(t.Token)
|
|
|
|
case *schema.UnionType:
|
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
|
|
|
// type for the input instead
|
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
|
|
|
return pkg.argsTypeImpl(typ.ElementType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "pulumi.Any"
|
|
|
|
default:
|
|
|
|
switch t {
|
|
|
|
case schema.BoolType:
|
|
|
|
return "pulumi.Bool"
|
|
|
|
case schema.IntType:
|
|
|
|
return "pulumi.Int"
|
|
|
|
case schema.NumberType:
|
|
|
|
return "pulumi.Float64"
|
|
|
|
case schema.StringType:
|
|
|
|
return "pulumi.String"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.ArchiveType:
|
2020-01-23 18:42:38 +00:00
|
|
|
return "pulumi.Archive"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.AssetType:
|
2020-01-23 18:42:38 +00:00
|
|
|
return "pulumi.AssetOrArchive"
|
2020-05-19 09:41:06 +00:00
|
|
|
case schema.JSONType:
|
|
|
|
fallthrough
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.AnyType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.Any"
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
panic(fmt.Errorf("unexpected type %T", t))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) argsType(t schema.Type) string {
|
|
|
|
return pkg.typeStringImpl(t, true)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
func (pkg *pkgContext) typeStringImpl(t schema.Type, argsType bool) string {
|
2020-01-21 22:45:48 +00:00
|
|
|
switch t := t.(type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.OptionalType:
|
|
|
|
if input, isInputType := t.ElementType.(*schema.InputType); isInputType {
|
|
|
|
elem := pkg.inputType(input.ElementType)
|
|
|
|
if isNilType(input.ElementType) || elem == "pulumi.Input" {
|
|
|
|
return elem
|
|
|
|
}
|
2022-07-06 18:35:31 +00:00
|
|
|
if pkg.isExternalReference(input.ElementType) {
|
|
|
|
_, details := pkg.contextForExternalReference(input.ElementType)
|
|
|
|
|
|
|
|
switch input.ElementType.(type) {
|
|
|
|
case *schema.ObjectType:
|
|
|
|
if !details.ptrInput {
|
|
|
|
return "*" + elem
|
|
|
|
}
|
|
|
|
case *schema.EnumType:
|
|
|
|
if !(details.ptrInput || details.input) {
|
|
|
|
return "*" + elem
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
if argsType {
|
|
|
|
return elem + "Ptr"
|
|
|
|
}
|
|
|
|
return strings.TrimSuffix(elem, "Input") + "PtrInput"
|
2021-01-08 00:28:19 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
|
|
|
|
elementType := pkg.typeStringImpl(t.ElementType, argsType)
|
|
|
|
if isNilType(t.ElementType) || elementType == "interface{}" {
|
|
|
|
return elementType
|
|
|
|
}
|
|
|
|
return "*" + elementType
|
|
|
|
case *schema.InputType:
|
|
|
|
if argsType {
|
|
|
|
return pkg.argsTypeImpl(t.ElementType)
|
|
|
|
}
|
|
|
|
return pkg.inputType(t.ElementType)
|
|
|
|
case *schema.EnumType:
|
2022-06-22 15:57:05 +00:00
|
|
|
return pkg.resolveEnumType(t)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ArrayType:
|
2021-06-24 16:17:55 +00:00
|
|
|
typ := "[]"
|
|
|
|
return typ + pkg.typeStringImpl(t.ElementType, argsType)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.MapType:
|
2021-06-24 16:17:55 +00:00
|
|
|
typ := "map[string]"
|
|
|
|
return typ + pkg.typeStringImpl(t.ElementType, argsType)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ObjectType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.resolveObjectType(t)
|
2020-11-09 18:55:53 +00:00
|
|
|
case *schema.ResourceType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "*" + pkg.resolveResourceType(t)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.TokenType:
|
|
|
|
// Use the underlying type for now.
|
|
|
|
if t.UnderlyingType != nil {
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.typeStringImpl(t.UnderlyingType, argsType)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.tokenToType(t.Token)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.UnionType:
|
2020-11-03 07:02:56 +00:00
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
2021-06-24 16:17:55 +00:00
|
|
|
// type for the enum instead
|
2020-11-03 07:02:56 +00:00
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.typeStringImpl(typ.ElementType, argsType)
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
// TODO(pdg): union types
|
2021-06-24 16:17:55 +00:00
|
|
|
return "interface{}"
|
2020-01-21 22:45:48 +00:00
|
|
|
default:
|
|
|
|
switch t {
|
|
|
|
case schema.BoolType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "bool"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.IntType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "int"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.NumberType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "float64"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.StringType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "string"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.ArchiveType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.Archive"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.AssetType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.AssetOrArchive"
|
2020-05-19 09:41:06 +00:00
|
|
|
case schema.JSONType:
|
|
|
|
fallthrough
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.AnyType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "interface{}"
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
panic(fmt.Errorf("unexpected type %T", t))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) typeString(t schema.Type) string {
|
2021-11-23 23:10:15 +00:00
|
|
|
s := pkg.typeStringImpl(t, false)
|
|
|
|
if s == "pulumi." {
|
|
|
|
return "pulumi.Any"
|
|
|
|
}
|
|
|
|
return s
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-01-29 06:48:12 +00:00
|
|
|
func (pkg *pkgContext) isExternalReference(t schema.Type) bool {
|
2022-07-06 18:35:31 +00:00
|
|
|
isExternal, _, _ := pkg.isExternalReferenceWithPackage(t)
|
2022-02-03 15:43:05 +00:00
|
|
|
return isExternal
|
|
|
|
}
|
|
|
|
|
2022-06-22 15:57:05 +00:00
|
|
|
// Return if `t` is external to `pkg`. If so, the associated foreign schema.Package is returned.
|
2022-07-06 18:35:31 +00:00
|
|
|
func (pkg *pkgContext) isExternalReferenceWithPackage(t schema.Type) (
|
2023-03-03 16:36:39 +00:00
|
|
|
isExternal bool, extPkg schema.PackageReference, token string,
|
|
|
|
) {
|
2021-01-29 06:48:12 +00:00
|
|
|
switch typ := t.(type) {
|
|
|
|
case *schema.ObjectType:
|
2022-12-08 10:45:46 +00:00
|
|
|
isExternal = typ.PackageReference != nil && !codegen.PkgEquals(typ.PackageReference, pkg.pkg)
|
2022-02-03 15:43:05 +00:00
|
|
|
if isExternal {
|
2022-12-08 10:45:46 +00:00
|
|
|
extPkg = typ.PackageReference
|
2022-07-06 18:35:31 +00:00
|
|
|
token = typ.Token
|
2022-02-03 15:43:05 +00:00
|
|
|
}
|
|
|
|
return
|
2021-01-29 06:48:12 +00:00
|
|
|
case *schema.ResourceType:
|
2022-12-08 10:45:46 +00:00
|
|
|
isExternal = typ.Resource != nil && pkg.pkg != nil && !codegen.PkgEquals(typ.Resource.PackageReference, pkg.pkg)
|
2022-02-03 15:43:05 +00:00
|
|
|
if isExternal {
|
2022-12-08 10:45:46 +00:00
|
|
|
extPkg = typ.Resource.PackageReference
|
2022-07-06 18:35:31 +00:00
|
|
|
token = typ.Token
|
2022-02-03 15:43:05 +00:00
|
|
|
}
|
|
|
|
return
|
2022-06-22 15:57:05 +00:00
|
|
|
case *schema.EnumType:
|
2022-12-08 10:45:46 +00:00
|
|
|
isExternal = pkg.pkg != nil && !codegen.PkgEquals(typ.PackageReference, pkg.pkg)
|
2022-06-22 15:57:05 +00:00
|
|
|
if isExternal {
|
2022-12-08 10:45:46 +00:00
|
|
|
extPkg = typ.PackageReference
|
2022-07-06 18:35:31 +00:00
|
|
|
token = typ.Token
|
2022-06-22 15:57:05 +00:00
|
|
|
}
|
|
|
|
return
|
2021-01-29 06:48:12 +00:00
|
|
|
}
|
2022-02-03 15:43:05 +00:00
|
|
|
return
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 18:06:57 +00:00
|
|
|
// resolveResourceType resolves resource references in properties while
|
|
|
|
// taking into account potential external resources. Returned type is
|
|
|
|
// always marked as required. Caller should check if the property is
|
|
|
|
// optional and convert the type to a pointer if necessary.
|
|
|
|
func (pkg *pkgContext) resolveResourceType(t *schema.ResourceType) string {
|
2021-01-29 06:48:12 +00:00
|
|
|
if !pkg.isExternalReference(t) {
|
2021-01-19 17:55:40 +00:00
|
|
|
return pkg.tokenToResource(t.Token)
|
|
|
|
}
|
2022-07-06 18:35:31 +00:00
|
|
|
extPkgCtx, _ := pkg.contextForExternalReference(t)
|
2021-01-19 17:55:40 +00:00
|
|
|
resType := extPkgCtx.tokenToResource(t.Token)
|
|
|
|
if !strings.Contains(resType, ".") {
|
2022-12-08 10:45:46 +00:00
|
|
|
resType = fmt.Sprintf("%s.%s", extPkgCtx.pkg.Name(), resType)
|
2021-01-19 17:55:40 +00:00
|
|
|
}
|
|
|
|
return resType
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// resolveObjectType resolves resource references in properties while
|
|
|
|
// taking into account potential external resources. Returned type is
|
|
|
|
// always marked as required. Caller should check if the property is
|
|
|
|
// optional and convert the type to a pointer if necessary.
|
|
|
|
func (pkg *pkgContext) resolveObjectType(t *schema.ObjectType) string {
|
2022-08-12 18:04:21 +00:00
|
|
|
isExternal, _, _ := pkg.isExternalReferenceWithPackage(t)
|
|
|
|
|
|
|
|
if !isExternal {
|
2021-06-24 16:17:55 +00:00
|
|
|
name := pkg.tokenToType(t.Token)
|
2023-12-04 12:34:40 +00:00
|
|
|
objectTypeDetails := pkg.detailsForType(t)
|
|
|
|
if t.IsInputShape() && objectTypeDetails.input {
|
2021-06-24 16:17:55 +00:00
|
|
|
return name + "Args"
|
|
|
|
}
|
|
|
|
return name
|
2021-01-19 17:55:40 +00:00
|
|
|
}
|
2023-12-04 12:34:40 +00:00
|
|
|
extPkg, externalTypeDetails := pkg.contextForExternalReference(t)
|
|
|
|
typeName := extPkg.tokenToType(t.Token)
|
|
|
|
if t.IsInputShape() && externalTypeDetails.input {
|
|
|
|
return typeName + "Args"
|
|
|
|
}
|
|
|
|
|
|
|
|
return typeName
|
2021-11-18 22:50:51 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 18:35:31 +00:00
|
|
|
func (pkg *pkgContext) contextForExternalReference(t schema.Type) (*pkgContext, typeDetails) {
|
|
|
|
isExternal, extPkg, token := pkg.isExternalReferenceWithPackage(t)
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(isExternal, "Expected external reference for %v", t)
|
2021-01-15 18:06:57 +00:00
|
|
|
|
2022-02-03 15:43:05 +00:00
|
|
|
var goInfo GoPackageInfo
|
2022-12-08 10:45:46 +00:00
|
|
|
extDef, err := extPkg.Definition()
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.AssertNoErrorf(err, "Could not load definition for %q", extPkg.Name())
|
|
|
|
contract.AssertNoErrorf(extDef.ImportLanguages(map[string]schema.Language{"go": Importer}),
|
|
|
|
"Failed to import languages")
|
2022-12-08 10:45:46 +00:00
|
|
|
if info, ok := extDef.Language["go"].(GoPackageInfo); ok {
|
2021-01-19 17:55:40 +00:00
|
|
|
goInfo = info
|
2022-02-03 15:43:05 +00:00
|
|
|
} else {
|
|
|
|
goInfo.ImportBasePath = extractImportBasePath(extPkg)
|
2021-01-19 17:55:40 +00:00
|
|
|
}
|
2022-02-03 15:43:05 +00:00
|
|
|
|
|
|
|
pkgImportAliases := goInfo.PackageImportAliases
|
|
|
|
|
|
|
|
// Ensure that any package import aliases we have specified locally take precedence over those
|
|
|
|
// specified in the remote package.
|
2022-12-08 10:45:46 +00:00
|
|
|
def, err := pkg.pkg.Definition()
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.AssertNoErrorf(err, "Could not load definition for %q", pkg.pkg.Name())
|
2022-12-08 10:45:46 +00:00
|
|
|
if ourPkgGoInfoI, has := def.Language["go"]; has {
|
2022-02-03 15:43:05 +00:00
|
|
|
ourPkgGoInfo := ourPkgGoInfoI.(GoPackageInfo)
|
|
|
|
if len(ourPkgGoInfo.PackageImportAliases) > 0 {
|
|
|
|
pkgImportAliases = make(map[string]string)
|
|
|
|
// Copy the external import aliases.
|
|
|
|
for k, v := range goInfo.PackageImportAliases {
|
|
|
|
pkgImportAliases[k] = v
|
|
|
|
}
|
|
|
|
// Copy the local import aliases, overwriting any external aliases.
|
|
|
|
for k, v := range ourPkgGoInfo.PackageImportAliases {
|
|
|
|
pkgImportAliases[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-06 18:35:31 +00:00
|
|
|
var maps map[string]*pkgContext
|
2022-08-12 18:04:21 +00:00
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
if extMap, ok := pkg.externalPackages.lookupContextMap(extDef); ok {
|
2022-07-06 18:35:31 +00:00
|
|
|
maps = extMap
|
|
|
|
} else {
|
2022-12-08 10:45:46 +00:00
|
|
|
maps, err = generatePackageContextMap(pkg.tool, extPkg, goInfo, pkg.externalPackages)
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.AssertNoErrorf(err, "Could not generate package context map")
|
2022-12-08 10:45:46 +00:00
|
|
|
pkg.externalPackages.setContextMap(extDef, maps)
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
2022-07-06 18:35:31 +00:00
|
|
|
extPkgCtx := maps[""]
|
|
|
|
extPkgCtx.pkgImportAliases = pkgImportAliases
|
2022-08-12 18:04:21 +00:00
|
|
|
extPkgCtx.externalPackages = pkg.externalPackages
|
2022-07-06 18:35:31 +00:00
|
|
|
mod := tokenToPackage(extPkg, goInfo.ModuleToPackage, token)
|
2022-11-22 20:44:19 +00:00
|
|
|
extPkgCtx.mod = ExternalModuleSig
|
2022-07-06 18:35:31 +00:00
|
|
|
|
|
|
|
return extPkgCtx, *maps[mod].detailsForType(t)
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
|
|
|
|
2021-12-03 20:28:43 +00:00
|
|
|
// outputTypeImpl does the meat of the generation of output type names from schema types. This function should only be
|
|
|
|
// called with a fully-resolved type (e.g. the result of codegen.ResolvedType). Instead of calling this function, you
|
|
|
|
// probably want to call pkgContext.outputType, which ensures that its argument is resolved.
|
|
|
|
func (pkg *pkgContext) outputTypeImpl(t schema.Type) string {
|
2020-01-21 22:45:48 +00:00
|
|
|
switch t := t.(type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.OptionalType:
|
2021-12-03 20:28:43 +00:00
|
|
|
elem := pkg.outputTypeImpl(t.ElementType)
|
2021-06-24 16:17:55 +00:00
|
|
|
if isNilType(t.ElementType) || elem == "pulumi.AnyOutput" {
|
|
|
|
return elem
|
|
|
|
}
|
2022-07-06 18:35:31 +00:00
|
|
|
if pkg.isExternalReference(t.ElementType) {
|
|
|
|
_, details := pkg.contextForExternalReference(t.ElementType)
|
|
|
|
switch t.ElementType.(type) {
|
|
|
|
case *schema.ObjectType:
|
|
|
|
if !details.ptrOutput {
|
|
|
|
return "*" + elem
|
|
|
|
}
|
|
|
|
case *schema.EnumType:
|
|
|
|
if !(details.ptrOutput || details.output) {
|
|
|
|
return "*" + elem
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return strings.TrimSuffix(elem, "Output") + "PtrOutput"
|
2020-11-03 07:02:56 +00:00
|
|
|
case *schema.EnumType:
|
2022-06-22 15:57:05 +00:00
|
|
|
return pkg.resolveEnumType(t) + "Output"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ArrayType:
|
2021-12-03 20:28:43 +00:00
|
|
|
en := strings.TrimSuffix(pkg.outputTypeImpl(t.ElementType), "Output")
|
2020-01-21 22:45:48 +00:00
|
|
|
if en == "pulumi.Any" {
|
|
|
|
return "pulumi.ArrayOutput"
|
|
|
|
}
|
|
|
|
return en + "ArrayOutput"
|
|
|
|
case *schema.MapType:
|
2021-12-03 20:28:43 +00:00
|
|
|
en := strings.TrimSuffix(pkg.outputTypeImpl(t.ElementType), "Output")
|
2020-01-21 22:45:48 +00:00
|
|
|
if en == "pulumi.Any" {
|
|
|
|
return "pulumi.MapOutput"
|
|
|
|
}
|
|
|
|
return en + "MapOutput"
|
|
|
|
case *schema.ObjectType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.resolveObjectType(t) + "Output"
|
2020-11-09 18:55:53 +00:00
|
|
|
case *schema.ResourceType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.resolveResourceType(t) + "Output"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.TokenType:
|
|
|
|
// Use the underlying type for now.
|
|
|
|
if t.UnderlyingType != nil {
|
2021-12-03 20:28:43 +00:00
|
|
|
return pkg.outputTypeImpl(t.UnderlyingType)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
return pkg.tokenToType(t.Token) + "Output"
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.UnionType:
|
2020-11-03 07:02:56 +00:00
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
|
|
|
// type for the output instead
|
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
2021-12-03 20:28:43 +00:00
|
|
|
return pkg.outputTypeImpl(typ.ElementType)
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
// TODO(pdg): union types
|
|
|
|
return "pulumi.AnyOutput"
|
2021-11-23 23:10:15 +00:00
|
|
|
case *schema.InputType:
|
|
|
|
// We can't make output types for input types. We instead strip the input and try again.
|
2021-12-03 20:28:43 +00:00
|
|
|
return pkg.outputTypeImpl(t.ElementType)
|
2020-01-21 22:45:48 +00:00
|
|
|
default:
|
|
|
|
switch t {
|
|
|
|
case schema.BoolType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.BoolOutput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.IntType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.IntOutput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.NumberType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.Float64Output"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.StringType:
|
2021-06-24 16:17:55 +00:00
|
|
|
return "pulumi.StringOutput"
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.ArchiveType:
|
|
|
|
return "pulumi.ArchiveOutput"
|
|
|
|
case schema.AssetType:
|
2020-01-23 18:00:18 +00:00
|
|
|
return "pulumi.AssetOrArchiveOutput"
|
2020-05-19 09:41:06 +00:00
|
|
|
case schema.JSONType:
|
|
|
|
fallthrough
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.AnyType:
|
|
|
|
return "pulumi.AnyOutput"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
panic(fmt.Errorf("unexpected type %T", t))
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func isAssetOrArchive(t schema.Type) bool {
|
|
|
|
switch t {
|
|
|
|
case schema.ArchiveType, schema.AssetType:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) genericElementType(schemaType schema.Type) (string, bool) {
|
|
|
|
switch schemaType {
|
|
|
|
case schema.StringType:
|
|
|
|
return "string", true
|
|
|
|
case schema.BoolType:
|
|
|
|
return "bool", true
|
|
|
|
case schema.IntType:
|
|
|
|
return "int", true
|
|
|
|
case schema.NumberType:
|
|
|
|
return "float64", true
|
|
|
|
case schema.ArchiveType:
|
|
|
|
return "pulumi.Archive", true
|
|
|
|
case schema.AssetType:
|
|
|
|
return "pulumi.AssetOrArchive", true
|
|
|
|
default:
|
|
|
|
switch schemaType := schemaType.(type) {
|
|
|
|
case *schema.ObjectType:
|
|
|
|
return pkg.resolveObjectType(schemaType), false
|
|
|
|
case *schema.EnumType:
|
|
|
|
return pkg.resolveEnumType(schemaType), true
|
|
|
|
case *schema.ResourceType:
|
|
|
|
return pkg.resolveResourceType(schemaType), false
|
|
|
|
case *schema.TokenType:
|
|
|
|
return pkg.genericElementType(schemaType.UnderlyingType)
|
|
|
|
case *schema.ArrayType:
|
|
|
|
elementType, _ := pkg.genericElementType(schemaType.ElementType)
|
2023-12-12 12:19:42 +00:00
|
|
|
return "[]" + elementType, false
|
2023-09-19 10:28:50 +00:00
|
|
|
case *schema.MapType:
|
|
|
|
elementType, _ := pkg.genericElementType(schemaType.ElementType)
|
2023-12-12 12:19:42 +00:00
|
|
|
return "map[string]" + elementType, false
|
2023-09-19 10:28:50 +00:00
|
|
|
case *schema.UnionType:
|
|
|
|
for _, e := range schemaType.ElementTypes {
|
|
|
|
if enumType, ok := e.(*schema.EnumType); ok {
|
|
|
|
return pkg.genericElementType(enumType.ElementType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "any", true
|
|
|
|
default:
|
|
|
|
return "any", true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// genericOutputTypeImpl is similar to outputTypeImpl, but it generates the generic variant.
|
|
|
|
// for example instead of pulumi.StringOutput, it generates pulumix.Output[string]
|
|
|
|
func (pkg *pkgContext) genericOutputTypeImpl(t schema.Type) string {
|
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.OptionalType:
|
|
|
|
elementType, isPrimitive := pkg.genericElementType(t.ElementType)
|
|
|
|
if elementType == "any" {
|
|
|
|
return fmt.Sprintf("pulumix.Output[%s]", elementType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isPrimitive {
|
|
|
|
// for example OptionalType{StringType} becomes pulumix.Output[*string]
|
|
|
|
return fmt.Sprintf("pulumix.Output[*%s]", elementType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if pkg.isExternalReference(t.ElementType) {
|
|
|
|
_, details := pkg.contextForExternalReference(t.ElementType)
|
|
|
|
switch t.ElementType.(type) {
|
|
|
|
case *schema.ObjectType:
|
|
|
|
if !details.ptrOutput {
|
|
|
|
return "*" + elementType
|
|
|
|
}
|
|
|
|
case *schema.EnumType:
|
|
|
|
if !(details.ptrOutput || details.output) {
|
|
|
|
return "*" + elementType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pkg.genericOutputTypeImpl(t.ElementType)
|
|
|
|
case *schema.EnumType:
|
|
|
|
elementType, _ := pkg.genericElementType(t)
|
|
|
|
return fmt.Sprintf("pulumix.Output[%s]", elementType)
|
|
|
|
case *schema.ArrayType:
|
|
|
|
elementType, isPrimitive := pkg.genericElementType(t.ElementType)
|
|
|
|
if isPrimitive {
|
|
|
|
return fmt.Sprintf("pulumix.ArrayOutput[%s]", elementType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// for non-primitive types such as objects and resources
|
|
|
|
// use GArrayOutput[Type, TypeOutput]
|
|
|
|
return fmt.Sprintf("pulumix.GArrayOutput[%s, %sOutput]", elementType, elementType)
|
|
|
|
case *schema.MapType:
|
|
|
|
elementType, isPrimitive := pkg.genericElementType(t.ElementType)
|
|
|
|
if isPrimitive {
|
|
|
|
return fmt.Sprintf("pulumix.MapOutput[%s]", elementType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// for non-primitive types such as objects and resources
|
|
|
|
// use GMapOutput[Type, TypeOutput]
|
|
|
|
return fmt.Sprintf("pulumix.GMapOutput[%s, %sOutput]", elementType, elementType)
|
|
|
|
case *schema.ObjectType:
|
|
|
|
objectTypeName, _ := pkg.genericElementType(t)
|
|
|
|
return fmt.Sprintf("pulumix.GPtrOutput[%s, %sOutput]", objectTypeName, objectTypeName)
|
|
|
|
case *schema.ResourceType:
|
|
|
|
resourceTypeName, _ := pkg.genericElementType(t)
|
|
|
|
// element type of a ResourceOutput is Resource
|
|
|
|
return fmt.Sprintf("pulumix.GPtrOutput[%s, %sOutput]", resourceTypeName, resourceTypeName)
|
|
|
|
case *schema.TokenType:
|
|
|
|
// Use the underlying type for now.
|
|
|
|
if t.UnderlyingType != nil {
|
|
|
|
return pkg.genericOutputType(t.UnderlyingType)
|
|
|
|
}
|
|
|
|
|
|
|
|
tokenType := pkg.tokenToType(t.Token)
|
|
|
|
return fmt.Sprintf("pulumix.Output[%s]", tokenType)
|
|
|
|
case *schema.UnionType:
|
|
|
|
// If the union is actually a relaxed enum type, use the underlying
|
|
|
|
// type for the enum instead
|
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if typ, ok := e.(*schema.EnumType); ok {
|
|
|
|
return pkg.genericOutputTypeImpl(typ.ElementType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO(pdg): union types
|
|
|
|
return "pulumix.Output[interface{}]"
|
|
|
|
case *schema.InputType:
|
|
|
|
// We can't make output types for input types. We instead strip the input and try again.
|
|
|
|
return pkg.genericOutputTypeImpl(t.ElementType)
|
|
|
|
default:
|
|
|
|
elementType, _ := pkg.genericElementType(t)
|
|
|
|
return fmt.Sprintf("pulumix.Output[%s]", elementType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-03 20:28:43 +00:00
|
|
|
// outputType returns a reference to the Go output type that corresponds to the given schema type. For example, given
|
|
|
|
// a schema.String, outputType returns "pulumi.String", and given a *schema.ObjectType with the token pkg:mod:Name,
|
|
|
|
// outputType returns "mod.NameOutput" or "NameOutput", depending on whether or not the object type lives in a
|
|
|
|
// different module than the one associated with the receiver.
|
|
|
|
func (pkg *pkgContext) outputType(t schema.Type) string {
|
|
|
|
return pkg.outputTypeImpl(codegen.ResolvedType(t))
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
// genericOutputType returns a reference to the Go output type that corresponds to the given schema type.
|
|
|
|
// For example, given a schema.StringType, genericOutputType returns "pulumix.Output[string]",
|
|
|
|
// and given a *schema.ObjectType with the token pkg:mod:Name,
|
|
|
|
// outputType returns "mod.NameOutput" or "NameOutput", depending on whether the object type lives in a
|
|
|
|
// different module than the one associated with the receiver.
|
|
|
|
func (pkg *pkgContext) genericOutputType(t schema.Type) string {
|
|
|
|
return pkg.genericOutputTypeImpl(codegen.ResolvedType(t))
|
|
|
|
}
|
|
|
|
|
2021-12-03 20:28:43 +00:00
|
|
|
// toOutputMethod returns the name of the "ToXXXOutput" method for the given schema type. For example, given a
|
|
|
|
// schema.String, toOutputMethod returns "ToStringOutput", and given a *schema.ObjectType with the token pkg:mod:Name,
|
|
|
|
// outputType returns "ToNameOutput".
|
|
|
|
func (pkg *pkgContext) toOutputMethod(t schema.Type) string {
|
|
|
|
outputTypeName := pkg.outputType(t)
|
|
|
|
if i := strings.LastIndexByte(outputTypeName, '.'); i != -1 {
|
|
|
|
outputTypeName = outputTypeName[i+1:]
|
|
|
|
}
|
|
|
|
return "To" + outputTypeName
|
|
|
|
}
|
|
|
|
|
2022-11-30 11:12:33 +00:00
|
|
|
// printComment filters examples for the Go languages and prepends double forward slash to each line in the given
|
|
|
|
// comment. If indent is true, each line is indented with tab character. It returns the number of lines in the
|
|
|
|
// resulting comment. It guarantees that each line is terminated with newline character.
|
2020-05-14 00:12:59 +00:00
|
|
|
func printComment(w io.Writer, comment string, indent bool) int {
|
2020-06-18 19:32:15 +00:00
|
|
|
comment = codegen.FilterExamples(comment, "go")
|
2020-05-14 00:12:59 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
lines := strings.Split(comment, "\n")
|
|
|
|
for len(lines) > 0 && lines[len(lines)-1] == "" {
|
|
|
|
lines = lines[:len(lines)-1]
|
|
|
|
}
|
|
|
|
for _, l := range lines {
|
|
|
|
if indent {
|
|
|
|
fmt.Fprintf(w, "\t")
|
|
|
|
}
|
2020-02-11 22:34:22 +00:00
|
|
|
if l == "" {
|
|
|
|
fmt.Fprintf(w, "//\n")
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "// %s\n", l)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2020-05-14 00:12:59 +00:00
|
|
|
return len(lines)
|
|
|
|
}
|
|
|
|
|
|
|
|
func printCommentWithDeprecationMessage(w io.Writer, comment, deprecationMessage string, indent bool) {
|
|
|
|
lines := printComment(w, comment, indent)
|
|
|
|
if deprecationMessage != "" {
|
|
|
|
if lines > 0 {
|
|
|
|
fmt.Fprintf(w, "//\n")
|
|
|
|
}
|
2023-12-12 12:19:42 +00:00
|
|
|
printComment(w, "Deprecated: "+deprecationMessage, indent)
|
2020-05-14 00:12:59 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
func (pkg *pkgContext) genInputInterface(w io.Writer, name string) {
|
|
|
|
printComment(w, pkg.getInputUsage(name), false)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "type %sInput interface {\n", name)
|
|
|
|
fmt.Fprintf(w, "\tpulumi.Input\n\n")
|
2020-04-07 00:01:33 +00:00
|
|
|
fmt.Fprintf(w, "\tTo%sOutput() %sOutput\n", Title(name), name)
|
|
|
|
fmt.Fprintf(w, "\tTo%sOutputWithContext(context.Context) %sOutput\n", Title(name), name)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-12-12 17:52:25 +00:00
|
|
|
func (pkg *pkgContext) genEnumInputInterface(w io.Writer, name string, enumType *schema.EnumType) {
|
|
|
|
enumCases := []string{}
|
|
|
|
for _, enumCase := range enumType.Elements {
|
|
|
|
if enumCase.DeprecationMessage != "" {
|
|
|
|
// skip deprecated enum cases
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
enumCases = append(enumCases, fmt.Sprintf("\t\t%s", enumCase.Name))
|
|
|
|
}
|
|
|
|
|
|
|
|
enumUsage := strings.Join([]string{
|
|
|
|
fmt.Sprintf("%sInput is an input type that accepts values of the %s enum", name, name),
|
|
|
|
fmt.Sprintf("A concrete instance of `%sInput` can be one of the following:", name),
|
|
|
|
"",
|
|
|
|
strings.Join(enumCases, "\n"),
|
|
|
|
" ",
|
|
|
|
}, "\n")
|
|
|
|
|
|
|
|
printComment(w, enumUsage, false)
|
|
|
|
fmt.Fprintf(w, "type %sInput interface {\n", name)
|
|
|
|
fmt.Fprintf(w, "\tpulumi.Input\n\n")
|
|
|
|
fmt.Fprintf(w, "\tTo%sOutput() %sOutput\n", Title(name), name)
|
|
|
|
fmt.Fprintf(w, "\tTo%sOutputWithContext(context.Context) %sOutput\n", Title(name), name)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
func (pkg *pkgContext) getUsageForNestedType(name, baseTypeName string) string {
|
2021-03-15 23:15:58 +00:00
|
|
|
const defaultExampleFormat = "%sArgs{...}"
|
|
|
|
example := fmt.Sprintf(defaultExampleFormat, baseTypeName)
|
|
|
|
|
|
|
|
trimmer := func(typeName string) string {
|
|
|
|
if strings.HasSuffix(typeName, "Array") {
|
|
|
|
return typeName[:strings.LastIndex(typeName, "Array")]
|
|
|
|
}
|
|
|
|
if strings.HasSuffix(typeName, "Map") {
|
|
|
|
return typeName[:strings.LastIndex(typeName, "Map")]
|
|
|
|
}
|
|
|
|
return typeName
|
|
|
|
}
|
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
// If not a nested collection type, use the default example format
|
|
|
|
if trimmer(name) == name {
|
|
|
|
return example
|
|
|
|
}
|
|
|
|
|
2021-03-15 23:15:58 +00:00
|
|
|
if strings.HasSuffix(name, "Map") {
|
2021-03-31 05:23:04 +00:00
|
|
|
if pkg.schemaNames.Has(baseTypeName) {
|
2021-03-15 23:15:58 +00:00
|
|
|
return fmt.Sprintf("%s{ \"key\": %s }", name, example)
|
|
|
|
}
|
2021-03-31 05:23:04 +00:00
|
|
|
return fmt.Sprintf("%s{ \"key\": %s }", name, pkg.getUsageForNestedType(baseTypeName, trimmer(baseTypeName)))
|
2021-03-15 23:15:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasSuffix(name, "Array") {
|
2021-03-31 05:23:04 +00:00
|
|
|
if pkg.schemaNames.Has(baseTypeName) {
|
2021-03-15 23:15:58 +00:00
|
|
|
return fmt.Sprintf("%s{ %s }", name, example)
|
|
|
|
}
|
2021-03-31 05:23:04 +00:00
|
|
|
return fmt.Sprintf("%s{ %s }", name, pkg.getUsageForNestedType(baseTypeName, trimmer(baseTypeName)))
|
2021-03-15 23:15:58 +00:00
|
|
|
}
|
|
|
|
return example
|
|
|
|
}
|
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
func (pkg *pkgContext) getInputUsage(name string) string {
|
2020-04-03 02:59:08 +00:00
|
|
|
if strings.HasSuffix(name, "Array") {
|
2021-03-15 23:15:58 +00:00
|
|
|
baseTypeName := name[:strings.LastIndex(name, "Array")]
|
2020-04-03 02:59:08 +00:00
|
|
|
return strings.Join([]string{
|
|
|
|
fmt.Sprintf("%sInput is an input type that accepts %s and %sOutput values.", name, name, name),
|
|
|
|
fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name),
|
|
|
|
"",
|
2021-03-31 05:23:04 +00:00
|
|
|
"\t\t " + pkg.getUsageForNestedType(name, baseTypeName),
|
2020-04-03 02:59:08 +00:00
|
|
|
" ",
|
|
|
|
}, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasSuffix(name, "Map") {
|
2021-03-15 23:15:58 +00:00
|
|
|
baseTypeName := name[:strings.LastIndex(name, "Map")]
|
2020-04-03 02:59:08 +00:00
|
|
|
return strings.Join([]string{
|
|
|
|
fmt.Sprintf("%sInput is an input type that accepts %s and %sOutput values.", name, name, name),
|
|
|
|
fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name),
|
|
|
|
"",
|
2021-03-31 05:23:04 +00:00
|
|
|
"\t\t " + pkg.getUsageForNestedType(name, baseTypeName),
|
2020-04-03 02:59:08 +00:00
|
|
|
" ",
|
|
|
|
}, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasSuffix(name, "Ptr") {
|
2021-03-15 23:15:58 +00:00
|
|
|
baseTypeName := name[:strings.LastIndex(name, "Ptr")]
|
2020-04-03 02:59:08 +00:00
|
|
|
return strings.Join([]string{
|
2021-03-15 23:15:58 +00:00
|
|
|
fmt.Sprintf("%sInput is an input type that accepts %sArgs, %s and %sOutput values.", name, baseTypeName, name, name),
|
2020-04-03 02:59:08 +00:00
|
|
|
fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name),
|
|
|
|
"",
|
2021-03-15 23:15:58 +00:00
|
|
|
fmt.Sprintf("\t\t %sArgs{...}", baseTypeName),
|
2020-04-03 02:59:08 +00:00
|
|
|
"",
|
|
|
|
" or:",
|
|
|
|
"",
|
|
|
|
"\t\t nil",
|
|
|
|
" ",
|
|
|
|
}, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join([]string{
|
|
|
|
fmt.Sprintf("%sInput is an input type that accepts %sArgs and %sOutput values.", name, name, name),
|
|
|
|
fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name),
|
|
|
|
"",
|
|
|
|
fmt.Sprintf("\t\t %sArgs{...}", name),
|
|
|
|
" ",
|
|
|
|
}, "\n")
|
|
|
|
}
|
|
|
|
|
2021-08-23 20:46:09 +00:00
|
|
|
type genInputImplementationArgs struct {
|
2023-09-19 10:28:50 +00:00
|
|
|
name string
|
|
|
|
receiverType string
|
|
|
|
elementType string
|
|
|
|
ptrMethods bool
|
|
|
|
toOutputMethods bool
|
|
|
|
usingGenericTypes bool
|
2023-11-04 12:17:41 +00:00
|
|
|
goPackageinfo GoPackageInfo
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
func (pkg *pkgContext) genInputImplementation(
|
|
|
|
w io.Writer,
|
|
|
|
name string,
|
|
|
|
receiverType string,
|
|
|
|
elementType string,
|
|
|
|
ptrMethods bool,
|
|
|
|
usingGenericTypes bool,
|
|
|
|
) {
|
2021-08-23 20:46:09 +00:00
|
|
|
genInputImplementationWithArgs(w, genInputImplementationArgs{
|
2023-09-19 10:28:50 +00:00
|
|
|
name: name,
|
|
|
|
receiverType: receiverType,
|
|
|
|
elementType: elementType,
|
|
|
|
ptrMethods: ptrMethods,
|
|
|
|
toOutputMethods: true,
|
|
|
|
usingGenericTypes: usingGenericTypes,
|
2023-11-04 12:17:41 +00:00
|
|
|
goPackageinfo: goPackageInfo(pkg.pkg),
|
2021-08-23 20:46:09 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func genInputImplementationWithArgs(w io.Writer, genArgs genInputImplementationArgs) {
|
|
|
|
name := genArgs.name
|
|
|
|
receiverType := genArgs.receiverType
|
|
|
|
elementType := genArgs.elementType
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "func (%s) ElementType() reflect.Type {\n", receiverType)
|
[codegen/go] Remove ResourcePtr input/output types (#8449)
These changes remove the `Ptr` variants of input/ouptut types for
resources. A `TPtr` input or output is normally generated for `T` if `T`
is present in an `optional(input(T))` or `optional(output(T))` and if
the Go representation for `T` is not nilable. The generation of `Ptr`
variants for resource types breaks the latter rule: the canonical
representation of a resource type named `Foo` is a pointer to a struct
type named `Foo` (i.e. `*Foo`). `Foo` itself is not a resource, as it
does not implement the Go `Resource` interface. Because this
representation already accommodates `nil` to indicate the lack of a
value, we need not generate `FooPtr{Input,Output}` types.
Besides being unnecessary, the implementation of `Ptr` types for
resources was incorrect. Rather than using `**Foo` as their element
type, these types use `*Foo`--identical to the element type used for
the normal input/output types. Furthermore, the generated code for
at least `FooOutput.ToFooPtrOutputWithContext` and `FooPtrOutput.Elem`
was incorrect, making these types virtually unusable in practice.
Finally, these `Ptr` types should never appear on input/output
properties in practice, as the logic we use to generate input and output
type references never generates them for `optional({input,output}(T)).
Instead, it generates references to the standard input/output types.
Though this is _technically_ a breaking change--it changes the set of
exported types for any package that defines resources--I believe that in
practice it will be invisible to users for the reasons stated above.
These types are not usable, and were never referenced.
This is preparatory work for #7943.
2021-11-23 18:24:56 +00:00
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s)(nil)).Elem()\n", elementType)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
2023-08-28 16:42:37 +00:00
|
|
|
var hasToOutput bool
|
2021-08-23 20:46:09 +00:00
|
|
|
if genArgs.toOutputMethods {
|
|
|
|
fmt.Fprintf(w, "func (i %s) To%sOutput() %sOutput {\n", receiverType, Title(name), name)
|
|
|
|
fmt.Fprintf(w, "\treturn i.To%sOutputWithContext(context.Background())\n", Title(name))
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2021-08-23 20:46:09 +00:00
|
|
|
fmt.Fprintf(w, "func (i %s) To%sOutputWithContext(ctx context.Context) %sOutput {\n", receiverType, Title(name), name)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumi.ToOutputWithContext(ctx, i).(%sOutput)\n", name)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2023-08-28 16:42:37 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if !genArgs.usingGenericTypes {
|
|
|
|
// Generate 'ToOuput(context.Context) pulumix.Output[T]' method
|
|
|
|
// to satisfy pulumix.Input[T].
|
2023-11-04 12:17:41 +00:00
|
|
|
if genArgs.goPackageinfo.Generics == GenericsSettingSideBySide {
|
|
|
|
fmt.Fprintf(w, "func (i %s) ToOutput(ctx context.Context) pulumix.Output[%s] {\n", receiverType, elementType)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Output[%s]{\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t\tOutputState: i.To%sOutputWithContext(ctx).OutputState,\n", Title(name))
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
hasToOutput = true
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
} else {
|
|
|
|
// Generate 'ToOuput(context.Context) pulumix.Output[T]' method which lifts the receiver type *T
|
|
|
|
// to satisfy pulumix.Input[*T].
|
|
|
|
fmt.Fprintf(w, "func (i *%s) ToOutput(ctx context.Context) pulumix.Output[*%s] {\n", receiverType, receiverType)
|
|
|
|
fmt.Fprint(w, "\treturn pulumix.Val(i)\n")
|
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if genArgs.ptrMethods && !genArgs.usingGenericTypes {
|
2020-04-07 00:01:33 +00:00
|
|
|
fmt.Fprintf(w, "func (i %s) To%sPtrOutput() %sPtrOutput {\n", receiverType, Title(name), name)
|
|
|
|
fmt.Fprintf(w, "\treturn i.To%sPtrOutputWithContext(context.Background())\n", Title(name))
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
2020-04-07 00:01:33 +00:00
|
|
|
fmt.Fprintf(w, "func (i %s) To%sPtrOutputWithContext(ctx context.Context) %sPtrOutput {\n", receiverType, Title(name), name)
|
2021-01-15 18:06:57 +00:00
|
|
|
if strings.HasSuffix(receiverType, "Args") {
|
|
|
|
fmt.Fprintf(w, "\treturn pulumi.ToOutputWithContext(ctx, i).(%[1]sOutput).To%[1]sPtrOutputWithContext(ctx)\n", name)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\treturn pulumi.ToOutputWithContext(ctx, i).(%sPtrOutput)\n", name)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2023-08-28 16:42:37 +00:00
|
|
|
|
|
|
|
if !hasToOutput {
|
2023-09-19 10:28:50 +00:00
|
|
|
// Generate 'ToOuput(context.Context) pulumix.Output[*T]' method
|
|
|
|
// to satisfy pulumix.Input[*T].
|
2023-11-04 12:17:41 +00:00
|
|
|
if genArgs.goPackageinfo.Generics == GenericsSettingSideBySide {
|
|
|
|
fmt.Fprintf(w, "func (i %s) ToOutput(ctx context.Context) pulumix.Output[*%s] {\n", receiverType, elementType)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Output[*%s]{\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t\tOutputState: i.To%sPtrOutputWithContext(ctx).OutputState,\n", Title(name))
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
2023-08-28 16:42:37 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
func (pkg *pkgContext) genOutputType(w io.Writer, baseName, elementType string, ptrMethods, usingGenericTypes bool) {
|
2021-08-02 20:43:24 +00:00
|
|
|
fmt.Fprintf(w, "type %sOutput struct { *pulumi.OutputState }\n\n", baseName)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (%sOutput) ElementType() reflect.Type {\n", baseName)
|
[codegen/go] Remove ResourcePtr input/output types (#8449)
These changes remove the `Ptr` variants of input/ouptut types for
resources. A `TPtr` input or output is normally generated for `T` if `T`
is present in an `optional(input(T))` or `optional(output(T))` and if
the Go representation for `T` is not nilable. The generation of `Ptr`
variants for resource types breaks the latter rule: the canonical
representation of a resource type named `Foo` is a pointer to a struct
type named `Foo` (i.e. `*Foo`). `Foo` itself is not a resource, as it
does not implement the Go `Resource` interface. Because this
representation already accommodates `nil` to indicate the lack of a
value, we need not generate `FooPtr{Input,Output}` types.
Besides being unnecessary, the implementation of `Ptr` types for
resources was incorrect. Rather than using `**Foo` as their element
type, these types use `*Foo`--identical to the element type used for
the normal input/output types. Furthermore, the generated code for
at least `FooOutput.ToFooPtrOutputWithContext` and `FooPtrOutput.Elem`
was incorrect, making these types virtually unusable in practice.
Finally, these `Ptr` types should never appear on input/output
properties in practice, as the logic we use to generate input and output
type references never generates them for `optional({input,output}(T)).
Instead, it generates references to the standard input/output types.
Though this is _technically_ a breaking change--it changes the set of
exported types for any package that defines resources--I believe that in
practice it will be invisible to users for the reasons stated above.
These types are not usable, and were never referenced.
This is preparatory work for #7943.
2021-11-23 18:24:56 +00:00
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s)(nil)).Elem()\n", elementType)
|
2021-08-02 20:43:24 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutput() %[1]sOutput {\n", baseName, Title(baseName))
|
|
|
|
fmt.Fprintf(w, "\treturn o\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutputWithContext(ctx context.Context) %[1]sOutput {\n", baseName, Title(baseName))
|
|
|
|
fmt.Fprintf(w, "\treturn o\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if ptrMethods && !usingGenericTypes {
|
2021-08-02 20:43:24 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutput() %[1]sPtrOutput {\n", baseName, Title(baseName))
|
|
|
|
fmt.Fprintf(w, "\treturn o.To%sPtrOutputWithContext(context.Background())\n", Title(baseName))
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutputWithContext(ctx context.Context) %[1]sPtrOutput {\n", baseName, Title(baseName))
|
|
|
|
fmt.Fprintf(w, "\treturn o.ApplyTWithContext(ctx, func(_ context.Context, v %[1]s) *%[1]s {\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t\treturn &v\n")
|
|
|
|
fmt.Fprintf(w, "\t}).(%sPtrOutput)\n", baseName)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
2023-08-28 16:42:37 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
// Generate 'ToOuput(context.Context) pulumix.Output[T]' method
|
|
|
|
// to satisfy pulumix.Input[T].
|
2023-11-04 12:17:41 +00:00
|
|
|
goPackageInfo := goPackageInfo(pkg.pkg)
|
2023-11-16 18:41:57 +00:00
|
|
|
if goPackageInfo.Generics == GenericsSettingSideBySide || goPackageInfo.Generics == GenericsSettingGenericsOnly {
|
2023-11-04 12:17:41 +00:00
|
|
|
fmt.Fprintf(w, "func (o %sOutput) ToOutput(ctx context.Context) pulumix.Output[%s] {\n", baseName, elementType)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Output[%s]{\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t\tOutputState: o.OutputState,\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
2021-08-02 20:43:24 +00:00
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
func (pkg *pkgContext) genArrayOutput(w io.Writer, baseName, elementType string) {
|
|
|
|
pkg.genOutputType(w, baseName+"Array", "[]"+elementType, false, false)
|
2021-08-02 20:43:24 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %[1]sArrayOutput) Index(i pulumi.IntInput) %[1]sOutput {\n", baseName)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumi.All(o, i).ApplyT(func (vs []interface{}) %s {\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t\treturn vs[0].([]%s)[vs[1].(int)]\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t}).(%sOutput)\n", baseName)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
func (pkg *pkgContext) genMapOutput(w io.Writer, baseName, elementType string) {
|
|
|
|
pkg.genOutputType(w, baseName+"Map", "map[string]"+elementType, false, false)
|
2021-08-02 20:43:24 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %[1]sMapOutput) MapIndex(k pulumi.StringInput) %[1]sOutput {\n", baseName)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumi.All(o, k).ApplyT(func (vs []interface{}) %s{\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t\treturn vs[0].(map[string]%s)[vs[1].(string)]\n", elementType)
|
|
|
|
fmt.Fprintf(w, "\t}).(%sOutput)\n", baseName)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
func (pkg *pkgContext) genPtrOutput(w io.Writer, baseName, elementType string) {
|
|
|
|
pkg.genOutputType(w, baseName+"Ptr", "*"+elementType, false, false)
|
2021-08-02 20:43:24 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %[1]sPtrOutput) Elem() %[1]sOutput {\n", baseName)
|
|
|
|
fmt.Fprintf(w, "\treturn o.ApplyT(func(v *%[1]s) %[1]s {\n", baseName)
|
|
|
|
fmt.Fprint(w, "\t\tif v != nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\t\treturn *v\n")
|
|
|
|
fmt.Fprint(w, "\t\t}\n")
|
|
|
|
fmt.Fprintf(w, "\t\tvar ret %s\n", baseName)
|
|
|
|
fmt.Fprint(w, "\t\treturn ret\n")
|
|
|
|
fmt.Fprintf(w, "\t}).(%sOutput)\n", baseName)
|
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genEnum(w io.Writer, enumType *schema.EnumType, usingGenericTypes bool) error {
|
2021-11-03 00:41:06 +00:00
|
|
|
name := pkg.tokenToEnum(enumType.Token)
|
2020-11-03 07:02:56 +00:00
|
|
|
|
2020-11-09 21:05:59 +00:00
|
|
|
mod := pkg.tokenToPackage(enumType.Token)
|
|
|
|
modPkg, ok := pkg.packages[mod]
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(ok, "Context for module %q not found", mod)
|
2021-11-03 00:41:06 +00:00
|
|
|
|
2020-11-03 07:02:56 +00:00
|
|
|
printCommentWithDeprecationMessage(w, enumType.Comment, "", false)
|
2021-07-07 23:25:26 +00:00
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
elementArgsType := pkg.argsTypeImpl(enumType.ElementType)
|
|
|
|
elementGoType := pkg.typeString(enumType.ElementType)
|
|
|
|
asFuncName := strings.TrimPrefix(elementArgsType, "pulumi.")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %s %s\n\n", name, elementGoType)
|
2020-11-03 07:02:56 +00:00
|
|
|
|
2021-01-19 17:55:40 +00:00
|
|
|
fmt.Fprintln(w, "const (")
|
2020-11-03 07:02:56 +00:00
|
|
|
for _, e := range enumType.Elements {
|
|
|
|
printCommentWithDeprecationMessage(w, e.Comment, e.DeprecationMessage, true)
|
2020-12-16 17:22:44 +00:00
|
|
|
|
2023-03-03 16:36:39 +00:00
|
|
|
elementName := e.Name
|
2020-11-03 07:02:56 +00:00
|
|
|
if e.Name == "" {
|
|
|
|
elementName = fmt.Sprintf("%v", e.Value)
|
|
|
|
}
|
2020-12-16 17:22:44 +00:00
|
|
|
enumName, err := makeSafeEnumName(elementName, name)
|
2020-11-18 17:44:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-12-16 17:22:44 +00:00
|
|
|
e.Name = enumName
|
2021-01-04 21:22:56 +00:00
|
|
|
contract.Assertf(!modPkg.names.Has(e.Name), "Name collision for enum constant: %s for %s",
|
2020-11-09 21:05:59 +00:00
|
|
|
e.Name, enumType.Token)
|
2020-12-16 17:22:44 +00:00
|
|
|
|
2020-11-03 07:02:56 +00:00
|
|
|
switch reflect.TypeOf(e.Value).Kind() {
|
|
|
|
case reflect.String:
|
2020-11-18 08:05:13 +00:00
|
|
|
fmt.Fprintf(w, "%s = %s(%q)\n", e.Name, name, e.Value)
|
2020-11-03 07:02:56 +00:00
|
|
|
default:
|
|
|
|
fmt.Fprintf(w, "%s = %s(%v)\n", e.Name, name, e.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintln(w, ")")
|
2021-07-07 23:25:26 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if usingGenericTypes {
|
|
|
|
// no need to generate the rest of the enum output/input types
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
details := pkg.detailsForType(enumType)
|
|
|
|
if details.input || details.ptrInput {
|
|
|
|
inputType := pkg.inputType(enumType)
|
|
|
|
pkg.genEnumInputFuncs(w, name, enumType, elementArgsType, inputType, asFuncName)
|
|
|
|
}
|
2021-07-07 23:25:26 +00:00
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.output || details.ptrOutput {
|
|
|
|
pkg.genEnumOutputTypes(w, name, elementArgsType, elementGoType, asFuncName)
|
|
|
|
}
|
|
|
|
if details.input || details.ptrInput {
|
|
|
|
pkg.genEnumInputTypes(w, name, enumType, elementGoType)
|
|
|
|
}
|
2021-07-07 23:25:26 +00:00
|
|
|
|
|
|
|
// Generate the array input.
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.arrayInput {
|
2021-07-07 23:25:26 +00:00
|
|
|
pkg.genInputInterface(w, name+"Array")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %[1]sArray []%[1]s\n\n", name)
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Array", name+"Array", "[]"+name, false, usingGenericTypes)
|
2021-07-07 23:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the map input.
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.mapInput {
|
2021-07-07 23:25:26 +00:00
|
|
|
pkg.genInputInterface(w, name+"Map")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %[1]sMap map[string]%[1]s\n\n", name)
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Map", name+"Map", "map[string]"+name, false, usingGenericTypes)
|
2021-07-07 23:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the array output
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.arrayOutput {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genArrayOutput(w, name, name)
|
2021-07-07 23:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the map output.
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.mapOutput {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genMapOutput(w, name, name)
|
2021-07-07 23:25:26 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 17:44:30 +00:00
|
|
|
return nil
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
func (pkg *pkgContext) genEnumOutputTypes(w io.Writer, name, elementArgsType, elementGoType, asFuncName string) {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genOutputType(w, name, name, true, false)
|
2021-07-07 23:25:26 +00:00
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutput() %[3]sOutput {\n", name, asFuncName, elementArgsType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprintf(w, "return o.To%sOutputWithContext(context.Background())\n", asFuncName)
|
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutputWithContext(ctx context.Context) %[3]sOutput {\n", name, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return o.ApplyTWithContext(ctx, func(_ context.Context, e %s) %s {\n", name, elementGoType)
|
|
|
|
fmt.Fprintf(w, "return %s(e)\n", elementGoType)
|
|
|
|
fmt.Fprintf(w, "}).(%sOutput)\n", elementArgsType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutput() %[3]sPtrOutput {\n", name, asFuncName, elementArgsType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprintf(w, "return o.To%sPtrOutputWithContext(context.Background())\n", asFuncName)
|
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutputWithContext(ctx context.Context) %[3]sPtrOutput {\n", name, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return o.ApplyTWithContext(ctx, func(_ context.Context, e %s) *%s {\n", name, elementGoType)
|
|
|
|
fmt.Fprintf(w, "v := %s(e)\n", elementGoType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprintf(w, "return &v\n")
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "}).(%sPtrOutput)\n", elementArgsType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genPtrOutput(w, name, name)
|
2021-07-07 23:25:26 +00:00
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sPtrOutput) To%[2]sPtrOutput() %[3]sPtrOutput {\n", name, asFuncName, elementArgsType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprintf(w, "return o.To%sPtrOutputWithContext(context.Background())\n", asFuncName)
|
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (o %[1]sPtrOutput) To%[2]sPtrOutputWithContext(ctx context.Context) %[3]sPtrOutput {\n", name, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return o.ApplyTWithContext(ctx, func(_ context.Context, e *%s) *%s {\n", name, elementGoType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprintf(w, "if e == nil {\n")
|
|
|
|
fmt.Fprintf(w, "return nil\n")
|
|
|
|
fmt.Fprintf(w, "}\n")
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "v := %s(*e)\n", elementGoType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprintf(w, "return &v\n")
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "}).(%sPtrOutput)\n", elementArgsType)
|
2021-07-13 19:54:19 +00:00
|
|
|
fmt.Fprint(w, "}\n\n")
|
2021-07-07 23:25:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
func (pkg *pkgContext) genEnumInputTypes(w io.Writer, name string, enumType *schema.EnumType, elementGoType string) {
|
2023-12-12 17:52:25 +00:00
|
|
|
pkg.genEnumInputInterface(w, name, enumType)
|
2021-07-07 23:25:26 +00:00
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
typeName := cgstrings.Camel(name)
|
|
|
|
fmt.Fprintf(w, "var %sPtrType = reflect.TypeOf((**%s)(nil)).Elem()\n", typeName, name)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %sPtrInput interface {\n", name)
|
|
|
|
fmt.Fprint(w, "pulumi.Input\n\n")
|
|
|
|
fmt.Fprintf(w, "To%[1]sPtrOutput() %[1]sPtrOutput\n", name)
|
|
|
|
fmt.Fprintf(w, "To%[1]sPtrOutputWithContext(context.Context) %[1]sPtrOutput\n", name)
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "type %sPtr %s\n", typeName, elementGoType)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func %[1]sPtr(v %[2]s) %[1]sPtrInput {\n", name, elementGoType)
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "return (*%sPtr)(&v)\n", typeName)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "func (*%sPtr) ElementType() reflect.Type {\n", typeName)
|
|
|
|
fmt.Fprintf(w, "return %sPtrType\n", typeName)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "func (in *%[1]sPtr) To%[2]sPtrOutput() %[2]sPtrOutput {\n", typeName, name)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "return pulumi.ToOutput(in).(%sPtrOutput)\n", name)
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "func (in *%[1]sPtr) To%[2]sPtrOutputWithContext(ctx context.Context) %[2]sPtrOutput {\n", cgstrings.Camel(name), name)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "return pulumi.ToOutputWithContext(ctx, in).(%sPtrOutput)\n", name)
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
fmt.Fprintln(w)
|
2023-08-28 16:42:37 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
// ToOutput implementation for pulumix.Input.
|
2023-08-28 16:42:37 +00:00
|
|
|
fmt.Fprintf(w, "func (in *%sPtr) ToOutput(ctx context.Context) pulumix.Output[*%s] {\n", typeName, name)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Output[*%s]{\n", name)
|
|
|
|
fmt.Fprintf(w, "\t\tOutputState: in.To%sPtrOutputWithContext(ctx).OutputState,\n", name)
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2021-07-07 23:25:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
func (pkg *pkgContext) genEnumInputFuncs(w io.Writer, typeName string, enum *schema.EnumType, elementArgsType, inputType, asFuncName string) {
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintln(w)
|
2020-11-03 07:02:56 +00:00
|
|
|
fmt.Fprintf(w, "func (%s) ElementType() reflect.Type {\n", typeName)
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "return reflect.TypeOf((*%s)(nil)).Elem()\n", typeName)
|
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[1]sOutput() %[1]sOutput {\n", typeName)
|
|
|
|
fmt.Fprintf(w, "return pulumi.ToOutput(e).(%sOutput)\n", typeName)
|
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[1]sOutputWithContext(ctx context.Context) %[1]sOutput {\n", typeName)
|
|
|
|
fmt.Fprintf(w, "return pulumi.ToOutputWithContext(ctx, e).(%sOutput)\n", typeName)
|
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[1]sPtrOutput() %[1]sPtrOutput {\n", typeName)
|
|
|
|
fmt.Fprintf(w, "return e.To%sPtrOutputWithContext(context.Background())\n", typeName)
|
2020-11-03 07:02:56 +00:00
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[1]sPtrOutputWithContext(ctx context.Context) %[1]sPtrOutput {\n", typeName)
|
|
|
|
fmt.Fprintf(w, "return %[1]s(e).To%[1]sOutputWithContext(ctx).To%[1]sPtrOutputWithContext(ctx)\n", typeName)
|
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[2]sOutput() %[3]sOutput {\n", typeName, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return pulumi.ToOutput(%[1]s(e)).(%[1]sOutput)\n", elementArgsType)
|
2020-11-03 07:02:56 +00:00
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[2]sOutputWithContext(ctx context.Context) %[3]sOutput {\n", typeName, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return pulumi.ToOutputWithContext(ctx, %[1]s(e)).(%[1]sOutput)\n", elementArgsType)
|
2020-11-03 07:02:56 +00:00
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[2]sPtrOutput() %[3]sPtrOutput {\n", typeName, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return %s(e).To%sPtrOutputWithContext(context.Background())\n", elementArgsType, asFuncName)
|
2020-11-03 07:02:56 +00:00
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
|
2021-11-03 00:41:06 +00:00
|
|
|
fmt.Fprintf(w, "func (e %[1]s) To%[2]sPtrOutputWithContext(ctx context.Context) %[3]sPtrOutput {\n", typeName, asFuncName, elementArgsType)
|
|
|
|
fmt.Fprintf(w, "return %[1]s(e).To%[2]sOutputWithContext(ctx).To%[2]sPtrOutputWithContext(ctx)\n", elementArgsType, asFuncName)
|
2020-11-03 07:02:56 +00:00
|
|
|
fmt.Fprintln(w, "}")
|
|
|
|
fmt.Fprintln(w)
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) assignProperty(
|
|
|
|
w io.Writer,
|
|
|
|
p *schema.Property,
|
|
|
|
object,
|
|
|
|
value string,
|
|
|
|
indirectAssign bool,
|
|
|
|
useGenericTypes bool,
|
|
|
|
) {
|
2021-11-23 23:10:15 +00:00
|
|
|
t := strings.TrimSuffix(pkg.typeString(p.Type), "Input")
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericTypes {
|
|
|
|
t = "pulumix.Val"
|
|
|
|
if isOptionalType(reduceInputType(p.Type)) {
|
|
|
|
t = "pulumix.Ptr"
|
|
|
|
}
|
|
|
|
}
|
2021-11-23 23:10:15 +00:00
|
|
|
switch codegen.UnwrapType(p.Type).(type) {
|
|
|
|
case *schema.EnumType:
|
2023-09-19 10:28:50 +00:00
|
|
|
if !useGenericTypes {
|
|
|
|
t = ""
|
|
|
|
}
|
2021-11-23 23:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if codegen.IsNOptionalInput(p.Type) {
|
|
|
|
if t != "" {
|
|
|
|
value = fmt.Sprintf("%s(%s)", t, value)
|
|
|
|
}
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "\t%s.%s = %s\n", object, pkg.fieldName(nil, p), value)
|
2021-11-23 23:10:15 +00:00
|
|
|
} else if indirectAssign {
|
2022-10-17 16:37:07 +00:00
|
|
|
tmpName := cgstrings.Camel(p.Name) + "_"
|
2021-11-23 23:10:15 +00:00
|
|
|
fmt.Fprintf(w, "%s := %s\n", tmpName, value)
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "%s.%s = &%s\n", object, pkg.fieldName(nil, p), tmpName)
|
2021-11-23 23:10:15 +00:00
|
|
|
} else {
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "%s.%s = %s\n", object, pkg.fieldName(nil, p), value)
|
2021-11-23 23:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-04 00:12:20 +00:00
|
|
|
func (pkg *pkgContext) fieldName(r *schema.Resource, field *schema.Property) string {
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(field != nil, "Field must not be nil")
|
2022-11-04 00:12:20 +00:00
|
|
|
return fieldName(pkg, r, field)
|
|
|
|
}
|
|
|
|
|
2020-05-14 00:12:59 +00:00
|
|
|
func (pkg *pkgContext) genPlainType(w io.Writer, name, comment, deprecationMessage string,
|
2023-03-03 16:36:39 +00:00
|
|
|
properties []*schema.Property,
|
|
|
|
) {
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, comment, deprecationMessage, false)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "type %s struct {\n", name)
|
|
|
|
for _, p := range properties {
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), pkg.typeString(codegen.ResolvedType(p.Type)), p.Name)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-11-21 16:43:51 +00:00
|
|
|
// genGenericPlainType is the same as genPlainType, but used for generic variant SDKs
|
|
|
|
// where it maintains optionalness of property types
|
|
|
|
func (pkg *pkgContext) genGenericPlainType(w io.Writer, name, comment, deprecationMessage string,
|
|
|
|
properties []*schema.Property,
|
|
|
|
) {
|
|
|
|
printCommentWithDeprecationMessage(w, comment, deprecationMessage, false)
|
|
|
|
fmt.Fprintf(w, "type %s struct {\n", name)
|
|
|
|
for _, p := range properties {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), pkg.plainGenericInputType(p.Type), p.Name)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2022-05-03 18:13:21 +00:00
|
|
|
func (pkg *pkgContext) genObjectDefaultFunc(w io.Writer, name string,
|
2023-03-03 16:36:39 +00:00
|
|
|
properties []*schema.Property,
|
2023-09-19 10:28:50 +00:00
|
|
|
useGenericTypes bool,
|
2023-03-03 16:36:39 +00:00
|
|
|
) error {
|
2021-11-23 23:10:15 +00:00
|
|
|
defaults := []*schema.Property{}
|
|
|
|
for _, p := range properties {
|
|
|
|
if p.DefaultValue != nil || codegen.IsProvideDefaultsFuncRequired(p.Type) {
|
|
|
|
defaults = append(defaults, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are no defaults, so we don't need to generate a defaults function.
|
|
|
|
if len(defaults) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
printComment(w, fmt.Sprintf("%s sets the appropriate defaults for %s", ProvideDefaultsMethodName, name), false)
|
|
|
|
fmt.Fprintf(w, "func (val *%[1]s) %[2]s() *%[1]s {\n", name, ProvideDefaultsMethodName)
|
2022-05-03 18:13:21 +00:00
|
|
|
fmt.Fprintf(w, "if val == nil {\n return nil\n}\n")
|
|
|
|
fmt.Fprintf(w, "tmp := *val\n")
|
2021-11-23 23:10:15 +00:00
|
|
|
for _, p := range defaults {
|
|
|
|
if p.DefaultValue != nil {
|
2023-02-08 00:12:30 +00:00
|
|
|
if isNilType(p.Type) {
|
|
|
|
fmt.Fprintf(w, "if tmp.%s == nil {\n", pkg.fieldName(nil, p))
|
|
|
|
} else {
|
2023-08-22 17:16:43 +00:00
|
|
|
fmt.Fprintf(w, "if %s.IsZero(tmp.%s) {\n", pkg.internalModuleName, pkg.fieldName(nil, p))
|
2023-02-08 00:12:30 +00:00
|
|
|
}
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
err := pkg.setDefaultValue(w, p.DefaultValue, codegen.UnwrapType(p.Type), func(w io.Writer, dv string) error {
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.assignProperty(w, p, "tmp", dv, !p.IsRequired(), useGenericTypes)
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-11-23 23:10:15 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
} else if funcName := pkg.provideDefaultsFuncName(p.Type); funcName != "" {
|
|
|
|
var member string
|
|
|
|
if codegen.IsNOptionalInput(p.Type) {
|
2022-05-03 18:13:21 +00:00
|
|
|
// f := fmt.Sprintf("func(v %[1]s) %[1]s { return *v.%[2]s() }", name, funcName)
|
2022-11-04 00:12:20 +00:00
|
|
|
// member = fmt.Sprintf("tmp.%[1]s.ApplyT(%[2]s)", pkg.fieldName(nil, p), f)
|
2021-11-23 23:10:15 +00:00
|
|
|
} else {
|
2022-11-04 00:12:20 +00:00
|
|
|
member = fmt.Sprintf("tmp.%[1]s.%[2]s()", pkg.fieldName(nil, p), funcName)
|
2022-05-03 18:13:21 +00:00
|
|
|
sigil := ""
|
|
|
|
if p.IsRequired() {
|
|
|
|
sigil = "*"
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.assignProperty(w, p, "tmp", sigil+member, false, useGenericTypes)
|
2021-11-23 23:10:15 +00:00
|
|
|
}
|
2022-05-03 18:13:21 +00:00
|
|
|
fmt.Fprintln(w)
|
2021-11-23 23:10:15 +00:00
|
|
|
} else {
|
|
|
|
panic(fmt.Sprintf("Property %s[%s] should not be in the default list", p.Name, p.Type.String()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "return &tmp\n}\n")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The name of the method used to instantiate defaults.
|
|
|
|
const ProvideDefaultsMethodName = "Defaults"
|
|
|
|
|
|
|
|
func (pkg *pkgContext) provideDefaultsFuncName(typ schema.Type) string {
|
|
|
|
if !codegen.IsProvideDefaultsFuncRequired(typ) {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return ProvideDefaultsMethodName
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genInputTypes(
|
|
|
|
w io.Writer,
|
|
|
|
t *schema.ObjectType,
|
|
|
|
details *typeDetails,
|
|
|
|
usingGenericTypes bool,
|
|
|
|
) error {
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(t.IsInputShape(), "Object type must have input shape")
|
2021-06-24 16:17:55 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
name := pkg.tokenToType(t.Token)
|
|
|
|
|
|
|
|
// Generate the plain inputs.
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.input {
|
2023-09-19 10:28:50 +00:00
|
|
|
if !usingGenericTypes {
|
|
|
|
pkg.genInputInterface(w, name)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2022-05-03 18:13:21 +00:00
|
|
|
inputName := name + "Args"
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.genInputArgsStruct(w, inputName, t, usingGenericTypes)
|
2022-05-03 18:13:21 +00:00
|
|
|
if !pkg.disableObjectDefaults {
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genObjectDefaultFunc(w, inputName, t.Properties, usingGenericTypes); err != nil {
|
2022-05-03 18:13:21 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name, inputName, name, details.ptrInput, usingGenericTypes)
|
2021-12-10 23:35:24 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
// Generate the pointer input.
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.ptrInput && !usingGenericTypes {
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg.genInputInterface(w, name+"Ptr")
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
ptrTypeName := cgstrings.Camel(name) + "PtrType"
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %s %sArgs\n\n", ptrTypeName, name)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func %[1]sPtr(v *%[1]sArgs) %[1]sPtrInput {", name)
|
|
|
|
fmt.Fprintf(w, "\treturn (*%s)(v)\n", ptrTypeName)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Ptr", "*"+ptrTypeName, "*"+name, false, usingGenericTypes)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the array input.
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.arrayInput && !pkg.names.Has(name+"Array") && !usingGenericTypes {
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg.genInputInterface(w, name+"Array")
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %[1]sArray []%[1]sInput\n\n", name)
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Array", name+"Array", "[]"+name, false, usingGenericTypes)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the map input.
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.mapInput && !pkg.names.Has(name+"Map") && !usingGenericTypes {
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg.genInputInterface(w, name+"Map")
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %[1]sMap map[string]%[1]sInput\n\n", name)
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Map", name+"Map", "map[string]"+name, false, usingGenericTypes)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2022-05-03 18:13:21 +00:00
|
|
|
return nil
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genInputArgsStruct(
|
|
|
|
w io.Writer,
|
|
|
|
typeName string,
|
|
|
|
t *schema.ObjectType,
|
|
|
|
useGenericTypes bool,
|
|
|
|
) {
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(t.IsInputShape(), "Object type must have input shape")
|
2021-08-23 20:46:09 +00:00
|
|
|
|
|
|
|
printComment(w, t.Comment, false)
|
|
|
|
fmt.Fprintf(w, "type %s struct {\n", typeName)
|
|
|
|
for _, p := range t.Properties {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
inputType := pkg.typeString(p.Type)
|
|
|
|
if useGenericTypes {
|
2023-11-21 16:43:51 +00:00
|
|
|
if p.Plain {
|
|
|
|
inputType = pkg.plainGenericInputType(p.Type)
|
|
|
|
} else {
|
|
|
|
inputType = pkg.genericInputType(p.Type)
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), inputType, p.Name)
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
type genOutputTypesArgs struct {
|
2023-09-19 10:28:50 +00:00
|
|
|
t *schema.ObjectType
|
|
|
|
usingGenericTypes bool
|
2021-08-23 20:46:09 +00:00
|
|
|
|
|
|
|
// optional type name override
|
2023-10-02 12:53:06 +00:00
|
|
|
name string
|
|
|
|
output bool
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) genOutputTypes(w io.Writer, genArgs genOutputTypesArgs) {
|
|
|
|
t := genArgs.t
|
|
|
|
details := pkg.detailsForType(t)
|
|
|
|
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(!t.IsInputShape(), "Object type must not have input shape")
|
2021-06-24 16:17:55 +00:00
|
|
|
|
2021-08-23 20:46:09 +00:00
|
|
|
name := genArgs.name
|
|
|
|
if name == "" {
|
|
|
|
name = pkg.tokenToType(t.Token)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
if details.output || genArgs.output {
|
2021-12-10 23:35:24 +00:00
|
|
|
printComment(w, t.Comment, false)
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genOutputType(w,
|
2023-09-19 10:28:50 +00:00
|
|
|
name, /* baseName */
|
|
|
|
name, /* elementType */
|
|
|
|
details.ptrInput, /* ptrMethods */
|
|
|
|
genArgs.usingGenericTypes, /* usingGenericTypes */
|
2021-12-10 23:35:24 +00:00
|
|
|
)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
for _, p := range t.Properties {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false)
|
|
|
|
outputType, applyType := pkg.outputType(p.Type), pkg.typeString(p.Type)
|
2023-09-19 10:28:50 +00:00
|
|
|
if genArgs.usingGenericTypes {
|
|
|
|
outputType = pkg.genericOutputType(p.Type)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2022-11-04 00:12:20 +00:00
|
|
|
propName := pkg.fieldName(nil, p)
|
2021-12-10 23:35:24 +00:00
|
|
|
switch strings.ToLower(p.Name) {
|
|
|
|
case "elementtype", "issecret":
|
|
|
|
propName = "Get" + propName
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "func (o %sOutput) %s() %s {\n", name, propName, outputType)
|
2023-09-19 10:28:50 +00:00
|
|
|
if !genArgs.usingGenericTypes {
|
|
|
|
fmt.Fprintf(w, "\treturn o.ApplyT(func (v %s) %s { return v.%s }).(%s)\n",
|
|
|
|
name, applyType, pkg.fieldName(nil, p), outputType)
|
|
|
|
} else {
|
|
|
|
needsCast := genericTypeNeedsExplicitCasting(outputType)
|
|
|
|
if !needsCast {
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Apply[%s](o, func (v %s) %s { return v.%s })\n",
|
2023-11-21 16:43:51 +00:00
|
|
|
name, name, pkg.plainGenericInputType(p.Type), pkg.fieldName(nil, p))
|
2023-09-19 10:28:50 +00:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\tvalue := pulumix.Apply[%s](o, func (v %s) %s { return v.%s })\n",
|
2023-11-21 16:43:51 +00:00
|
|
|
name, name, pkg.plainGenericInputType(p.Type), pkg.fieldName(nil, p))
|
2023-09-19 10:28:50 +00:00
|
|
|
fmt.Fprintf(w, "\treturn %s{OutputState: value.OutputState}\n", outputType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2021-01-27 07:16:42 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.ptrOutput && !genArgs.usingGenericTypes {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genPtrOutput(w, name, name)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
for _, p := range t.Properties {
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false)
|
2021-06-24 16:17:55 +00:00
|
|
|
optionalType := codegen.OptionalType(p)
|
|
|
|
outputType, applyType := pkg.outputType(optionalType), pkg.typeString(optionalType)
|
2020-04-21 20:33:38 +00:00
|
|
|
deref := ""
|
|
|
|
// If the property was required, but the type it needs to return is an explicit pointer type, then we need
|
2021-05-27 23:02:19 +00:00
|
|
|
// to dereference it, unless it is a resource type which should remain a pointer.
|
|
|
|
_, isResourceType := p.Type.(*schema.ResourceType)
|
2021-06-24 16:17:55 +00:00
|
|
|
if p.IsRequired() && applyType[0] == '*' && !isResourceType {
|
2020-04-21 20:33:38 +00:00
|
|
|
deref = "&"
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
funcName := Title(p.Name)
|
|
|
|
// Avoid conflicts with Output interface for lifted attributes.
|
|
|
|
switch funcName {
|
|
|
|
case "IsSecret", "ElementType":
|
|
|
|
funcName = funcName + "Prop"
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %sPtrOutput) %s() %s {\n", name, funcName, outputType)
|
2020-04-21 20:33:38 +00:00
|
|
|
fmt.Fprintf(w, "\treturn o.ApplyT(func (v *%s) %s {\n", name, applyType)
|
|
|
|
fmt.Fprintf(w, "\t\tif v == nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\t\treturn nil\n")
|
|
|
|
fmt.Fprintf(w, "\t\t}\n")
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "\t\treturn %sv.%s\n", deref, pkg.fieldName(nil, p))
|
2020-04-21 20:33:38 +00:00
|
|
|
fmt.Fprintf(w, "\t}).(%s)\n", outputType)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.arrayOutput && !pkg.names.Has(name+"Array") && !genArgs.usingGenericTypes {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genArrayOutput(w, name, name)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.mapOutput && !pkg.names.Has(name+"Map") && !genArgs.usingGenericTypes {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genMapOutput(w, name, name)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func goPrimitiveValue(value interface{}) (string, error) {
|
|
|
|
v := reflect.ValueOf(value)
|
|
|
|
if v.Kind() == reflect.Interface {
|
|
|
|
v = v.Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch v.Kind() {
|
|
|
|
case reflect.Bool:
|
|
|
|
if v.Bool() {
|
|
|
|
return "true", nil
|
|
|
|
}
|
|
|
|
return "false", nil
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
|
|
|
|
return strconv.FormatInt(v.Int(), 10), nil
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
|
|
|
return strconv.FormatUint(v.Uint(), 10), nil
|
|
|
|
case reflect.Float32, reflect.Float64:
|
2021-10-27 00:18:48 +00:00
|
|
|
value := strconv.FormatFloat(v.Float(), 'f', -1, 64)
|
|
|
|
if !strings.ContainsRune(value, '.') {
|
|
|
|
value += ".0"
|
|
|
|
}
|
|
|
|
return value, nil
|
2020-01-21 22:45:48 +00:00
|
|
|
case reflect.String:
|
|
|
|
return fmt.Sprintf("%q", v.String()), nil
|
|
|
|
default:
|
2021-11-13 02:37:17 +00:00
|
|
|
return "", fmt.Errorf("unsupported default value of type %T", value)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-08 16:33:01 +00:00
|
|
|
func (pkg *pkgContext) getConstValue(cv interface{}) (string, error) {
|
|
|
|
var val string
|
|
|
|
if cv != nil {
|
|
|
|
v, err := goPrimitiveValue(cv)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
val = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return val, nil
|
|
|
|
}
|
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
// setDefaultValue generates a statement that assigns the default value of a
|
|
|
|
// property to a variable.
|
|
|
|
//
|
|
|
|
// The assign function is invoked with an expression that evaluates to the
|
|
|
|
// default value.
|
|
|
|
// It should return a statement that assigns that default value to the relevant
|
|
|
|
// variable.
|
|
|
|
// For example,
|
|
|
|
//
|
|
|
|
// err := pkg.setDefaultValue(w, dv, t, func(w io.Writer, value string) error {
|
|
|
|
// _, err := fmt.Fprintf(w, "v.%s = %s", fieldName, value)
|
|
|
|
// return err
|
|
|
|
// })
|
|
|
|
func (pkg *pkgContext) setDefaultValue(
|
|
|
|
w io.Writer,
|
|
|
|
dv *schema.DefaultValue,
|
|
|
|
t schema.Type,
|
|
|
|
assign func(io.Writer, string) error,
|
|
|
|
) error {
|
|
|
|
contract.Requiref(dv.Value != nil || len(dv.Environment) > 0,
|
|
|
|
"dv", "must have either a value or an environment variable override")
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
var val string
|
|
|
|
if dv.Value != nil {
|
|
|
|
v, err := goPrimitiveValue(dv.Value)
|
|
|
|
if err != nil {
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
return err
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
val = v
|
2021-11-23 23:10:15 +00:00
|
|
|
switch t.(type) {
|
|
|
|
case *schema.EnumType:
|
|
|
|
typeName := strings.TrimSuffix(pkg.typeString(codegen.UnwrapType(t)), "Input")
|
|
|
|
val = fmt.Sprintf("%s(%s)", typeName, val)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
if len(dv.Environment) == 0 {
|
|
|
|
// If there's no environment variable override,
|
|
|
|
// assign and we're done.
|
|
|
|
return assign(w, val)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
// For environment variable override, we will assign only
|
|
|
|
// if the environment variable is set.
|
2020-01-21 22:45:48 +00:00
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
parser, typ := "nil", "string"
|
|
|
|
switch codegen.UnwrapType(t).(type) {
|
|
|
|
case *schema.ArrayType:
|
2023-12-12 12:19:42 +00:00
|
|
|
parser, typ = pkg.internalModuleName+".ParseEnvStringArray", "pulumi.StringArray"
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
}
|
|
|
|
switch t {
|
|
|
|
case schema.BoolType:
|
2023-12-12 12:19:42 +00:00
|
|
|
parser, typ = pkg.internalModuleName+".ParseEnvBool", "bool"
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
case schema.IntType:
|
2023-12-12 12:19:42 +00:00
|
|
|
parser, typ = pkg.internalModuleName+".ParseEnvInt", "int"
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
case schema.NumberType:
|
2023-12-12 12:19:42 +00:00
|
|
|
parser, typ = pkg.internalModuleName+".ParseEnvFloat", "float64"
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
if val == "" {
|
|
|
|
// If there's no explicit default value,
|
|
|
|
// use nil so that we can assign conditionally.
|
|
|
|
val = "nil"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Roughly, we generate:
|
|
|
|
//
|
2023-06-12 23:23:54 +00:00
|
|
|
// if d := internal.getEnvOrDefault(defaultValue, parser, "ENV_VAR"); d != nil {
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
// $assign(d.(type))
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// This has the following effect:
|
|
|
|
//
|
|
|
|
// - if an environment variable was set, read from that
|
|
|
|
// - if a default value was specified, use that
|
|
|
|
// - otherwise, leave the variable unset
|
2023-08-22 17:16:43 +00:00
|
|
|
fmt.Fprintf(w, "if d := %s.GetEnvOrDefault(%s, %s", pkg.internalModuleName, val, parser)
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
for _, e := range dv.Environment {
|
|
|
|
fmt.Fprintf(w, ", %q", e)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "); d != nil {\n\t")
|
|
|
|
if err := assign(w, fmt.Sprintf("d.(%v)", typ)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
return nil
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genResource(
|
|
|
|
w io.Writer,
|
|
|
|
r *schema.Resource,
|
|
|
|
generateResourceContainerTypes bool,
|
|
|
|
useGenericVariant bool,
|
|
|
|
) error {
|
2021-09-04 02:42:45 +00:00
|
|
|
name := disambiguatedResourceName(r, pkg)
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, r.Comment, r.DeprecationMessage, false)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "type %s struct {\n", name)
|
|
|
|
|
2020-11-09 18:55:53 +00:00
|
|
|
switch {
|
|
|
|
case r.IsProvider:
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.ProviderResourceState\n\n")
|
2020-11-09 18:55:53 +00:00
|
|
|
case r.IsComponent:
|
|
|
|
fmt.Fprintf(w, "\tpulumi.ResourceState\n\n")
|
|
|
|
default:
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.CustomResourceState\n\n")
|
|
|
|
}
|
2020-11-09 18:55:53 +00:00
|
|
|
|
2021-05-26 22:00:51 +00:00
|
|
|
var secretProps []*schema.Property
|
2022-02-26 22:26:16 +00:00
|
|
|
var secretInputProps []*schema.Property
|
2021-09-08 05:23:30 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
for _, p := range r.Properties {
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
outputType := pkg.outputType(p.Type)
|
|
|
|
if useGenericVariant {
|
|
|
|
outputType = pkg.genericOutputType(p.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(r, p), outputType, p.Name)
|
2020-07-02 19:30:10 +00:00
|
|
|
|
|
|
|
if p.Secret {
|
2021-05-26 22:00:51 +00:00
|
|
|
secretProps = append(secretProps, p)
|
2020-07-02 19:30:10 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
// Create a constructor function that registers a new instance of this resource.
|
|
|
|
fmt.Fprintf(w, "// New%s registers a new resource with the given unique name, arguments, and options.\n", name)
|
|
|
|
fmt.Fprintf(w, "func New%s(ctx *pulumi.Context,\n", name)
|
|
|
|
fmt.Fprintf(w, "\tname string, args *%[1]sArgs, opts ...pulumi.ResourceOption) (*%[1]s, error) {\n", name)
|
|
|
|
|
|
|
|
// Ensure required arguments are present.
|
2020-11-03 07:02:56 +00:00
|
|
|
hasRequired := false
|
2020-01-21 22:45:48 +00:00
|
|
|
for _, p := range r.InputProperties {
|
2021-06-24 16:17:55 +00:00
|
|
|
if p.IsRequired() {
|
2020-11-03 07:02:56 +00:00
|
|
|
hasRequired = true
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-09 22:18:41 +00:00
|
|
|
// Various validation checks
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tif args == nil {\n")
|
2020-11-03 07:02:56 +00:00
|
|
|
if !hasRequired {
|
|
|
|
fmt.Fprintf(w, "\t\targs = &%sArgs{}\n", name)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(w, "\t\treturn nil, errors.New(\"missing one or more required arguments\")")
|
|
|
|
}
|
2020-11-09 22:18:41 +00:00
|
|
|
fmt.Fprintf(w, "\t}\n\n")
|
2020-11-03 07:02:56 +00:00
|
|
|
|
2020-11-09 22:18:41 +00:00
|
|
|
// Produce the inputs.
|
2021-11-23 23:10:15 +00:00
|
|
|
|
|
|
|
// Check all required inputs are present
|
2020-11-03 07:02:56 +00:00
|
|
|
for _, p := range r.InputProperties {
|
2021-07-07 23:25:26 +00:00
|
|
|
if p.IsRequired() && isNilType(p.Type) && p.DefaultValue == nil {
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "\tif args.%s == nil {\n", pkg.fieldName(r, p))
|
|
|
|
fmt.Fprintf(w, "\t\treturn nil, errors.New(\"invalid value for required argument '%s'\")\n", pkg.fieldName(r, p))
|
2021-07-07 23:25:26 +00:00
|
|
|
fmt.Fprintf(w, "\t}\n")
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
2022-02-26 22:26:16 +00:00
|
|
|
|
|
|
|
if p.Secret {
|
|
|
|
secretInputProps = append(secretInputProps, p)
|
|
|
|
}
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
assign := func(w io.Writer, p *schema.Property, value string) {
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.assignProperty(w, p, "args", value, isNilType(p.Type), useGenericVariant)
|
2021-10-27 00:18:48 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
for _, p := range r.InputProperties {
|
2020-04-08 16:33:01 +00:00
|
|
|
if p.ConstValue != nil {
|
|
|
|
v, err := pkg.getConstValue(p.ConstValue)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
assign(w, p, v)
|
2021-10-27 00:18:48 +00:00
|
|
|
} else if p.DefaultValue != nil {
|
2023-02-08 00:12:30 +00:00
|
|
|
if isNilType(p.Type) {
|
|
|
|
fmt.Fprintf(w, "\tif args.%s == nil {\n", pkg.fieldName(r, p))
|
|
|
|
} else {
|
2023-08-22 17:16:43 +00:00
|
|
|
fmt.Fprintf(w, "\tif %s.IsZero(args.%s) {\n", pkg.internalModuleName, pkg.fieldName(r, p))
|
2023-02-08 00:12:30 +00:00
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
err := pkg.setDefaultValue(w, p.DefaultValue, codegen.UnwrapType(p.Type), func(w io.Writer, dv string) error {
|
|
|
|
assign(w, p, dv)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-10-27 00:18:48 +00:00
|
|
|
fmt.Fprintf(w, "\t}\n")
|
2021-11-23 23:10:15 +00:00
|
|
|
} else if name := pkg.provideDefaultsFuncName(p.Type); name != "" && !pkg.disableObjectDefaults {
|
2023-09-19 10:28:50 +00:00
|
|
|
|
2021-12-03 20:28:43 +00:00
|
|
|
optionalDeref := ""
|
|
|
|
if p.IsRequired() {
|
|
|
|
optionalDeref = "*"
|
2021-11-23 23:10:15 +00:00
|
|
|
}
|
2021-12-03 20:28:43 +00:00
|
|
|
|
|
|
|
toOutputMethod := pkg.toOutputMethod(p.Type)
|
|
|
|
outputType := pkg.outputType(p.Type)
|
|
|
|
resolvedType := pkg.typeString(codegen.ResolvedType(p.Type))
|
2022-11-04 00:12:20 +00:00
|
|
|
originalValue := fmt.Sprintf("args.%s.%s()", pkg.fieldName(r, p), toOutputMethod)
|
2021-12-03 20:28:43 +00:00
|
|
|
valueWithDefaults := fmt.Sprintf("%[1]v.ApplyT(func (v %[2]s) %[2]s { return %[3]sv.%[4]s() }).(%[5]s)",
|
|
|
|
originalValue, resolvedType, optionalDeref, name, outputType)
|
2022-04-29 16:54:42 +00:00
|
|
|
if p.Plain {
|
2022-11-04 00:12:20 +00:00
|
|
|
valueWithDefaults = fmt.Sprintf("args.%v.Defaults()", pkg.fieldName(r, p))
|
2022-04-29 16:54:42 +00:00
|
|
|
}
|
2021-12-03 20:28:43 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericVariant {
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "if args.%s != nil {\n", pkg.fieldName(r, p))
|
2023-09-19 10:28:50 +00:00
|
|
|
t := p.Type
|
|
|
|
optionalPointer := ""
|
|
|
|
if isOptionalType(reduceInputType(t)) && !isArrayType(codegen.UnwrapType(t)) && !isMapType(codegen.UnwrapType(t)) {
|
|
|
|
optionalPointer = "*"
|
|
|
|
}
|
|
|
|
|
|
|
|
inputType := pkg.genericInputTypeImpl(t)
|
|
|
|
if strings.HasPrefix(inputType, "*") {
|
|
|
|
optionalPointer = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "args.%s = pulumix.Apply(args.%s, func(o %s%s) %s%s { return o.Defaults() })\n",
|
|
|
|
pkg.fieldName(r, p),
|
|
|
|
pkg.fieldName(r, p),
|
|
|
|
optionalPointer,
|
|
|
|
inputType,
|
|
|
|
optionalPointer,
|
|
|
|
inputType)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "}\n")
|
2021-11-23 23:10:15 +00:00
|
|
|
} else {
|
2023-09-19 10:28:50 +00:00
|
|
|
if !p.IsRequired() {
|
|
|
|
fmt.Fprintf(w, "if args.%s != nil {\n", pkg.fieldName(r, p))
|
|
|
|
fmt.Fprintf(w, "args.%[1]s = %s\n", pkg.fieldName(r, p), valueWithDefaults)
|
|
|
|
fmt.Fprint(w, "}\n")
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "args.%[1]s = %s\n", pkg.fieldName(r, p), valueWithDefaults)
|
|
|
|
}
|
2021-11-23 23:10:15 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-24 17:18:21 +00:00
|
|
|
// Set any defined aliases.
|
|
|
|
if len(r.Aliases) > 0 {
|
|
|
|
fmt.Fprintf(w, "\taliases := pulumi.Aliases([]pulumi.Alias{\n")
|
|
|
|
for _, alias := range r.Aliases {
|
|
|
|
s := "\t\t{\n"
|
|
|
|
if alias.Type != nil {
|
|
|
|
s += fmt.Sprintf("\t\t\tType: pulumi.String(%q),\n", *alias.Type)
|
|
|
|
}
|
|
|
|
s += "\t\t},\n"
|
|
|
|
fmt.Fprint(w, s)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t})\n")
|
|
|
|
fmt.Fprintf(w, "\topts = append(opts, aliases)\n")
|
|
|
|
}
|
2021-09-08 05:23:30 +00:00
|
|
|
|
|
|
|
// Setup secrets
|
2022-02-26 22:26:16 +00:00
|
|
|
for _, p := range secretInputProps {
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "\tif args.%s != nil {\n", pkg.fieldName(r, p))
|
fix(sdkgen/go): illegal cast in resource constructors when secret-wrapping input arguments
Codegen for wrapping input properties as secrets performed an incorrect cast, as
seen in Pulumi's AWS classic SDK.
Using the sample program and the resource constructor described in #11664 as our
test case, from `pulumi-aws/sdk/v5/go/aws/secretmanager/secretVersion.go`:
```go
func NewSecretVersion(ctx *pulumi.Context,
name string, args *SecretVersionArgs, opts ...pulumi.ResourceOption) (*SecretVersion, error) {
if args == nil {
return nil, errors.New("missing one or more required arguments")
}
if args.SecretId == nil {
return nil, errors.New("invalid value for required argument 'SecretId'")
}
if args.SecretBinary != nil {
82: args.SecretBinary = pulumi.ToSecret(args.SecretBinary).(pulumi.StringPtrOutput)
}
if args.SecretString != nil {
85: args.SecretString = pulumi.ToSecret(args.SecretString).(pulumi.StringPtrOutput)
}
```
`args.SecretBinary` is of type `pulumi.StringPtrInput` and `pulumi.ToSecret`
returns `pulumi.Output` returns its input as an Output-wrapped value marked
secret.
As `StringPtrInput` is an interface accepting multiple input types, the return
value would be either `pulumi.StringOutput` `pulumi.StringPtrOutput`. These are
both concrete types, and casting to the incorrect one would panic.
Fortunately we can cast back to the arg's type, as verified by building the new
codegen and testing the Pulumi program in #11664. This should handle regular
inputs and plain inputs.
The new codegen below converts an input type `T` to `pulumi.Output`, then casts
back to `T`.
```go
func NewSecretVersion(ctx *pulumi.Context,
// ...
if args.SecretBinary != nil {
82: args.SecretBinary = pulumi.ToSecret(args.SecretBinary).(pulumi.StringPtrInput)
}
if args.SecretString != nil {
85: args.SecretString = pulumi.ToSecret(args.SecretString).(pulumi.StringPtrInput)
}
```
2022-12-16 23:39:54 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if !useGenericVariant {
|
|
|
|
fmt.Fprintf(w, "\t\targs.%[1]s = pulumi.ToSecret(args.%[1]s).(%[2]s)\n",
|
|
|
|
pkg.fieldName(r, p),
|
|
|
|
pkg.typeString(p.Type))
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\t\tuntypedSecretValue := pulumi.ToSecret(args.%s.ToOutput(ctx.Context()).Untyped())\n",
|
|
|
|
pkg.fieldName(r, p))
|
|
|
|
|
|
|
|
t := p.Type
|
|
|
|
optionalPointer := ""
|
|
|
|
if isOptionalType(reduceInputType(t)) && !isArrayType(codegen.UnwrapType(t)) && !isMapType(codegen.UnwrapType(t)) {
|
|
|
|
optionalPointer = "*"
|
|
|
|
}
|
|
|
|
|
|
|
|
inputType := pkg.genericInputTypeImpl(t)
|
|
|
|
if strings.HasPrefix(inputType, "*") {
|
|
|
|
optionalPointer = ""
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t\targs.%s = pulumix.MustConvertTyped[%s%s](untypedSecretValue)\n",
|
|
|
|
pkg.fieldName(r, p),
|
|
|
|
optionalPointer,
|
|
|
|
inputType)
|
|
|
|
}
|
|
|
|
|
2022-02-26 22:26:16 +00:00
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
}
|
2020-07-02 19:30:10 +00:00
|
|
|
if len(secretProps) > 0 {
|
|
|
|
fmt.Fprintf(w, "\tsecrets := pulumi.AdditionalSecretOutputs([]string{\n")
|
|
|
|
for _, sp := range secretProps {
|
2021-05-26 22:00:51 +00:00
|
|
|
fmt.Fprintf(w, "\t\t\t%q,\n", sp.Name)
|
2020-07-02 19:30:10 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t})\n")
|
|
|
|
fmt.Fprintf(w, "\topts = append(opts, secrets)\n")
|
|
|
|
}
|
2020-03-24 17:18:21 +00:00
|
|
|
|
2021-09-08 05:23:30 +00:00
|
|
|
// Setup replaceOnChange
|
|
|
|
replaceOnChangesProps, errList := r.ReplaceOnChanges()
|
|
|
|
for _, err := range errList {
|
|
|
|
cmdutil.Diag().Warningf(&diag.Diag{Message: err.Error()})
|
|
|
|
}
|
|
|
|
replaceOnChangesStrings := schema.PropertyListJoinToString(replaceOnChangesProps,
|
|
|
|
func(x string) string { return x })
|
|
|
|
if len(replaceOnChangesProps) > 0 {
|
|
|
|
fmt.Fprint(w, "\treplaceOnChanges := pulumi.ReplaceOnChanges([]string{\n")
|
|
|
|
for _, p := range replaceOnChangesStrings {
|
|
|
|
fmt.Fprintf(w, "\t\t%q,\n", p)
|
|
|
|
}
|
|
|
|
fmt.Fprint(w, "\t})\n")
|
|
|
|
fmt.Fprint(w, "\topts = append(opts, replaceOnChanges)\n")
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
err := pkg.GenPkgDefaultsOptsCall(w, false /*invoke*/)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-01-11 01:38:54 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
// Finally make the call to registration.
|
|
|
|
fmt.Fprintf(w, "\tvar resource %s\n", name)
|
2020-11-09 18:55:53 +00:00
|
|
|
if r.IsComponent {
|
|
|
|
fmt.Fprintf(w, "\terr := ctx.RegisterRemoteComponentResource(\"%s\", name, args, &resource, opts...)\n", r.Token)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\terr := ctx.RegisterResource(\"%s\", name, args, &resource, opts...)\n", r.Token)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tif err != nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\treturn nil, err\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
fmt.Fprintf(w, "\treturn &resource, nil\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
// Emit a factory function that reads existing instances of this resource.
|
2020-11-09 18:55:53 +00:00
|
|
|
if !r.IsProvider && !r.IsComponent {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "// Get%[1]s gets an existing %[1]s resource's state with the given name, ID, and optional\n", name)
|
|
|
|
fmt.Fprintf(w, "// state properties that are used to uniquely qualify the lookup (nil if not required).\n")
|
|
|
|
fmt.Fprintf(w, "func Get%s(ctx *pulumi.Context,\n", name)
|
2020-03-07 01:50:18 +00:00
|
|
|
fmt.Fprintf(w, "\tname string, id pulumi.IDInput, state *%[1]sState, opts ...pulumi.ResourceOption) (*%[1]s, error) {\n", name)
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tvar resource %s\n", name)
|
|
|
|
fmt.Fprintf(w, "\terr := ctx.ReadResource(\"%s\", name, id, state, &resource, opts...)\n", r.Token)
|
|
|
|
fmt.Fprintf(w, "\tif err != nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\treturn nil, err\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
fmt.Fprintf(w, "\treturn &resource, nil\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
// Emit the state types for get methods.
|
|
|
|
fmt.Fprintf(w, "// Input properties used for looking up and filtering %s resources.\n", name)
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "type %sState struct {\n", cgstrings.Camel(name))
|
2021-06-24 16:17:55 +00:00
|
|
|
if r.StateInputs != nil {
|
|
|
|
for _, p := range r.StateInputs.Properties {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2022-11-04 00:12:20 +00:00
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(r, p), pkg.typeString(codegen.ResolvedType(codegen.OptionalType(p))), p.Name)
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "type %sState struct {\n", name)
|
2021-06-24 16:17:55 +00:00
|
|
|
if r.StateInputs != nil {
|
|
|
|
for _, p := range r.StateInputs.Properties {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
inputType := pkg.inputType(p.Type)
|
|
|
|
if useGenericVariant {
|
|
|
|
inputType = pkg.genericInputType(codegen.OptionalType(p))
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t%s %s\n", pkg.fieldName(r, p), inputType)
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (%sState) ElementType() reflect.Type {\n", name)
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%sState)(nil)).Elem()\n", cgstrings.Camel(name))
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit the args types.
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "type %sArgs struct {\n", cgstrings.Camel(name))
|
2020-01-21 22:45:48 +00:00
|
|
|
for _, p := range r.InputProperties {
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
inputTypeName := pkg.typeString(codegen.ResolvedType(p.Type))
|
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(r, p), inputTypeName, p.Name)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "// The set of arguments for constructing a %s resource.\n", name)
|
|
|
|
fmt.Fprintf(w, "type %sArgs struct {\n", name)
|
|
|
|
for _, p := range r.InputProperties {
|
2022-04-29 16:54:42 +00:00
|
|
|
typ := p.Type
|
|
|
|
if p.Plain {
|
2022-05-03 18:13:21 +00:00
|
|
|
typ = codegen.MapOptionalType(typ, func(typ schema.Type) schema.Type {
|
|
|
|
if input, ok := typ.(*schema.InputType); ok {
|
|
|
|
return input.ElementType
|
2022-04-29 16:54:42 +00:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
inputTypeName := pkg.inputType(typ)
|
|
|
|
if p.Plain {
|
|
|
|
inputTypeName = pkg.typeString(typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
if useGenericVariant {
|
|
|
|
if p.Plain {
|
|
|
|
inputTypeName = pkg.typeString(codegen.ResolvedType(typ))
|
|
|
|
} else {
|
|
|
|
inputTypeName = pkg.genericInputType(typ)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
fmt.Fprintf(w, "\t%s %s\n", pkg.fieldName(r, p), inputTypeName)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (%sArgs) ElementType() reflect.Type {\n", name)
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%sArgs)(nil)).Elem()\n", cgstrings.Camel(name))
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
|
|
|
// Emit resource methods.
|
|
|
|
for _, method := range r.Methods {
|
|
|
|
methodName := Title(method.Name)
|
|
|
|
f := method.Function
|
|
|
|
|
2023-01-11 22:17:14 +00:00
|
|
|
var objectReturnType *schema.ObjectType
|
|
|
|
if f.ReturnType != nil {
|
|
|
|
if objectType, ok := f.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
|
|
|
objectReturnType = objectType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
liftReturn := pkg.liftSingleValueMethodReturns && objectReturnType != nil && len(objectReturnType.Properties) == 1
|
2021-10-01 18:33:02 +00:00
|
|
|
|
2021-07-08 03:11:40 +00:00
|
|
|
var args []*schema.Property
|
|
|
|
if f.Inputs != nil {
|
|
|
|
for _, arg := range f.Inputs.InputShape.Properties {
|
|
|
|
if arg.Name == "__self__" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
args = append(args, arg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now emit the method signature.
|
|
|
|
argsig := "ctx *pulumi.Context"
|
|
|
|
if len(args) > 0 {
|
|
|
|
argsig = fmt.Sprintf("%s, args *%s%sArgs", argsig, name, methodName)
|
|
|
|
}
|
|
|
|
var retty string
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
if f.ReturnTypePlain {
|
|
|
|
if objectReturnType == nil {
|
|
|
|
t := pkg.typeString(codegen.ResolvedType(f.ReturnType))
|
|
|
|
retty = fmt.Sprintf("(o %s, e error)", t)
|
|
|
|
} else {
|
|
|
|
retty = fmt.Sprintf("(o %s%sResult, e error)", name, methodName)
|
|
|
|
}
|
|
|
|
} else if objectReturnType == nil {
|
2021-07-08 03:11:40 +00:00
|
|
|
retty = "error"
|
2023-01-11 22:17:14 +00:00
|
|
|
} else if liftReturn {
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericVariant {
|
|
|
|
retty = fmt.Sprintf("(%s, error)", pkg.genericOutputType(objectReturnType.Properties[0].Type))
|
|
|
|
} else {
|
|
|
|
retty = fmt.Sprintf("(%s, error)", pkg.outputType(objectReturnType.Properties[0].Type))
|
|
|
|
}
|
2021-07-08 03:11:40 +00:00
|
|
|
} else {
|
|
|
|
retty = fmt.Sprintf("(%s%sResultOutput, error)", name, methodName)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
printCommentWithDeprecationMessage(w, f.Comment, f.DeprecationMessage, false)
|
|
|
|
fmt.Fprintf(w, "func (r *%s) %s(%s) %s {\n", name, methodName, argsig, retty)
|
|
|
|
|
|
|
|
resultVar := "_"
|
2023-01-11 22:17:14 +00:00
|
|
|
if objectReturnType != nil {
|
2021-07-08 03:11:40 +00:00
|
|
|
resultVar = "out"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a map of inputs to pass to the runtime function.
|
|
|
|
inputsVar := "nil"
|
|
|
|
if len(args) > 0 {
|
|
|
|
inputsVar = "args"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now simply invoke the runtime function with the arguments.
|
|
|
|
outputsType := "pulumi.AnyOutput"
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
if objectReturnType != nil || f.ReturnTypePlain {
|
2023-01-11 22:17:14 +00:00
|
|
|
if liftReturn {
|
2022-10-17 16:37:07 +00:00
|
|
|
outputsType = fmt.Sprintf("%s%sResultOutput", cgstrings.Camel(name), methodName)
|
2021-10-01 18:33:02 +00:00
|
|
|
} else {
|
|
|
|
outputsType = fmt.Sprintf("%s%sResultOutput", name, methodName)
|
|
|
|
}
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
|
|
|
|
if !f.ReturnTypePlain {
|
|
|
|
fmt.Fprintf(w, "\t%s, err := ctx.Call(%q, %s, %s{}, r)\n", resultVar, f.Token, inputsVar, outputsType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.ReturnTypePlain {
|
|
|
|
// single-value returning methods use a magic property "res" on the wire
|
|
|
|
property := ""
|
|
|
|
if objectReturnType == nil {
|
|
|
|
property = cgstrings.UppercaseFirst("res")
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\tinternal.CallPlain(ctx, %q, %s, %s{}, r, %q, reflect.ValueOf(&o), &e)\n",
|
|
|
|
f.Token, inputsVar, outputsType, property)
|
|
|
|
fmt.Fprintf(w, "\treturn\n")
|
|
|
|
} else if objectReturnType == nil {
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "\treturn err\n")
|
2023-01-11 22:17:14 +00:00
|
|
|
} else if liftReturn {
|
2021-10-01 18:33:02 +00:00
|
|
|
// Check the error before proceeding.
|
|
|
|
fmt.Fprintf(w, "\tif err != nil {\n")
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericVariant {
|
|
|
|
fmt.Fprint(w, "\t\treturn nil, err\n")
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\t\treturn %s{}, err\n", pkg.outputType(objectReturnType.Properties[0].Type))
|
|
|
|
}
|
|
|
|
|
2021-10-01 18:33:02 +00:00
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
|
|
|
|
// Get the name of the method to return the output
|
2023-01-11 22:17:14 +00:00
|
|
|
fmt.Fprintf(w, "\treturn %s.(%s).%s(), nil\n", resultVar, cgstrings.Camel(outputsType), Title(objectReturnType.Properties[0].Name))
|
2021-07-08 03:11:40 +00:00
|
|
|
} else {
|
|
|
|
// Check the error before proceeding.
|
|
|
|
fmt.Fprintf(w, "\tif err != nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\treturn %s{}, err\n", outputsType)
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
|
|
|
|
// Return the result.
|
|
|
|
fmt.Fprintf(w, "\treturn %s.(%s), nil\n", resultVar, outputsType)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
|
|
|
// If there are argument and/or return types, emit them.
|
|
|
|
if len(args) > 0 {
|
|
|
|
fmt.Fprintf(w, "\n")
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "type %s%sArgs struct {\n", cgstrings.Camel(name), methodName)
|
2021-07-08 03:11:40 +00:00
|
|
|
for _, p := range args {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
inputTypeName := pkg.typeString(codegen.ResolvedType(p.Type))
|
|
|
|
if useGenericVariant {
|
|
|
|
inputTypeName = pkg.genericInputType(codegen.ResolvedType(p.Type))
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), inputTypeName, p.Name)
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "// The set of arguments for the %s method of the %s resource.\n", methodName, name)
|
|
|
|
fmt.Fprintf(w, "type %s%sArgs struct {\n", name, methodName)
|
|
|
|
for _, p := range args {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true)
|
2023-09-19 10:28:50 +00:00
|
|
|
inputTypeName := pkg.typeString(p.Type)
|
|
|
|
if useGenericVariant {
|
|
|
|
inputTypeName = pkg.genericInputType(codegen.ResolvedType(p.Type))
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t%s %s\n", pkg.fieldName(nil, p), inputTypeName)
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (%s%sArgs) ElementType() reflect.Type {\n", name, methodName)
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s%sArgs)(nil)).Elem()\n", cgstrings.Camel(name), methodName)
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
if objectReturnType != nil || f.ReturnTypePlain {
|
2021-10-01 18:33:02 +00:00
|
|
|
outputStructName := name
|
|
|
|
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
var comment string
|
|
|
|
var properties []*schema.Property
|
|
|
|
if f.ReturnTypePlain && objectReturnType == nil {
|
|
|
|
properties = []*schema.Property{
|
|
|
|
{
|
|
|
|
Name: "res",
|
|
|
|
Type: f.ReturnType,
|
|
|
|
Plain: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
properties = objectReturnType.Properties
|
|
|
|
comment = objectReturnType.Comment
|
|
|
|
}
|
|
|
|
|
2021-10-01 18:33:02 +00:00
|
|
|
// Don't export the result struct if we're lifting the value
|
2023-01-11 22:17:14 +00:00
|
|
|
if liftReturn {
|
2022-10-17 16:37:07 +00:00
|
|
|
outputStructName = cgstrings.Camel(name)
|
2021-10-01 18:33:02 +00:00
|
|
|
}
|
|
|
|
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "\n")
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
pkg.genPlainType(w, fmt.Sprintf("%s%sResult", outputStructName, methodName), comment, "", properties)
|
2021-07-08 03:11:40 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "\n")
|
2021-10-01 18:33:02 +00:00
|
|
|
fmt.Fprintf(w, "type %s%sResultOutput struct{ *pulumi.OutputState }\n\n", outputStructName, methodName)
|
2021-07-08 03:11:40 +00:00
|
|
|
|
2021-10-01 18:33:02 +00:00
|
|
|
fmt.Fprintf(w, "func (%s%sResultOutput) ElementType() reflect.Type {\n", outputStructName, methodName)
|
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s%sResult)(nil)).Elem()\n", outputStructName, methodName)
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
for _, p := range properties {
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "\n")
|
2023-09-19 10:28:50 +00:00
|
|
|
outputTypeName := pkg.outputType(p.Type)
|
|
|
|
if useGenericVariant {
|
|
|
|
outputTypeName = pkg.genericOutputType(p.Type)
|
|
|
|
}
|
2021-07-08 03:11:40 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false)
|
2021-10-01 18:33:02 +00:00
|
|
|
fmt.Fprintf(w, "func (o %s%sResultOutput) %s() %s {\n", outputStructName, methodName, Title(p.Name),
|
2023-09-19 10:28:50 +00:00
|
|
|
outputTypeName)
|
|
|
|
if !useGenericVariant {
|
|
|
|
fmt.Fprintf(w, "\treturn o.ApplyT(func (v %s%sResult) %s { return v.%s }).(%s)\n", outputStructName, methodName,
|
|
|
|
pkg.typeString(codegen.ResolvedType(p.Type)), Title(p.Name), outputTypeName)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Apply(o, func(v %s%sResult) %s { return v.%s })\n", outputStructName, methodName,
|
|
|
|
pkg.typeString(codegen.ResolvedType(p.Type)), Title(p.Name))
|
|
|
|
}
|
|
|
|
|
2021-07-08 03:11:40 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
}
|
|
|
|
}
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if !useGenericVariant {
|
|
|
|
// Emit the resource input type.
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
fmt.Fprintf(w, "type %sInput interface {\n", name)
|
|
|
|
fmt.Fprintf(w, "\tpulumi.Input\n\n")
|
|
|
|
fmt.Fprintf(w, "\tTo%[1]sOutput() %[1]sOutput\n", name)
|
|
|
|
fmt.Fprintf(w, "\tTo%[1]sOutputWithContext(ctx context.Context) %[1]sOutput\n", name)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2021-01-15 18:06:57 +00:00
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name, "*"+name, "*"+name, false, false)
|
2021-01-15 18:06:57 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if generateResourceContainerTypes && !r.IsProvider {
|
|
|
|
// Generate the resource array input.
|
|
|
|
pkg.genInputInterface(w, name+"Array")
|
|
|
|
fmt.Fprintf(w, "type %[1]sArray []%[1]sInput\n\n", name)
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Array", name+"Array", "[]*"+name, false, false)
|
2021-01-20 00:47:42 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
// Generate the resource map input.
|
|
|
|
pkg.genInputInterface(w, name+"Map")
|
|
|
|
fmt.Fprintf(w, "type %[1]sMap map[string]%[1]sInput\n\n", name)
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name+"Map", name+"Map", "map[string]*"+name, false, false)
|
2023-09-19 10:28:50 +00:00
|
|
|
}
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
2020-11-09 18:55:53 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
outputElementType := "*" + name
|
|
|
|
if useGenericVariant {
|
|
|
|
outputElementType = name
|
|
|
|
}
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genOutputType(w, name, outputElementType, false, useGenericVariant)
|
2021-01-20 00:47:42 +00:00
|
|
|
|
2022-05-03 18:36:57 +00:00
|
|
|
// Emit chaining methods for the resource output type.
|
|
|
|
for _, p := range r.Properties {
|
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false)
|
|
|
|
outputType := pkg.outputType(p.Type)
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericVariant {
|
|
|
|
outputType = pkg.genericOutputType(p.Type)
|
|
|
|
}
|
2022-05-03 18:36:57 +00:00
|
|
|
|
2022-11-04 00:12:20 +00:00
|
|
|
propName := pkg.fieldName(r, p)
|
2022-05-03 18:36:57 +00:00
|
|
|
switch strings.ToLower(p.Name) {
|
|
|
|
case "elementtype", "issecret":
|
|
|
|
propName = "Get" + propName
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "func (o %sOutput) %s() %s {\n", name, propName, outputType)
|
2023-09-19 10:28:50 +00:00
|
|
|
if !useGenericVariant {
|
|
|
|
fmt.Fprintf(w, "\treturn o.ApplyT(func (v *%s) %s { return v.%s }).(%s)\n",
|
|
|
|
name, outputType, pkg.fieldName(r, p), outputType)
|
|
|
|
} else {
|
|
|
|
needsCast := genericTypeNeedsExplicitCasting(outputType)
|
|
|
|
|
|
|
|
elementType := pkg.typeString(codegen.ResolvedType(p.Type))
|
|
|
|
|
|
|
|
if strings.HasPrefix(outputType, "pulumix.GPtrOutput") && !strings.HasPrefix(elementType, "*") {
|
|
|
|
elementType = "*" + elementType
|
|
|
|
}
|
|
|
|
|
|
|
|
isOptionalAssetOrArchive := isOptionalType(reduceInputType(p.Type)) &&
|
|
|
|
isAssetOrArchive(codegen.UnwrapType(p.Type))
|
|
|
|
if isOptionalAssetOrArchive && !strings.HasPrefix(elementType, "*") {
|
|
|
|
elementType = "*" + elementType
|
|
|
|
}
|
|
|
|
|
|
|
|
if needsCast {
|
|
|
|
// needs an explicit cast operation to align the types
|
|
|
|
fmt.Fprintf(w, "\tvalue := pulumix.Apply[%s](o, func (v %s) %s { return v.%s })\n",
|
|
|
|
name, name, outputType, pkg.fieldName(r, p))
|
|
|
|
fmt.Fprintf(w, "\tunwrapped := pulumix.Flatten[%s, %s](value)\n",
|
|
|
|
elementType, outputType)
|
|
|
|
fmt.Fprintf(w, "\treturn %s{OutputState: unwrapped.OutputState}\n", outputType)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\tvalue := pulumix.Apply[%s](o, func (v %s) %s { return v.%s })\n",
|
|
|
|
name, name, outputType, pkg.fieldName(r, p))
|
|
|
|
if !p.Plain {
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Flatten[%s, %s](value)\n",
|
|
|
|
elementType, outputType)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\treturn value\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-03 18:36:57 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if generateResourceContainerTypes && !r.IsProvider && !useGenericVariant {
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genArrayOutput(w, name, "*"+name)
|
|
|
|
pkg.genMapOutput(w, name, "*"+name)
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
2021-08-02 20:43:24 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.genResourceRegistrations(w, r, generateResourceContainerTypes, useGenericVariant)
|
2020-11-09 18:55:53 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
func goPackageInfo(packageReference schema.PackageReference) GoPackageInfo {
|
|
|
|
if packageReference == nil {
|
|
|
|
return GoPackageInfo{}
|
|
|
|
}
|
2021-09-30 18:56:42 +00:00
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
def, err := packageReference.Definition()
|
|
|
|
contract.AssertNoErrorf(err, "Could not load definition for %q", packageReference.Name())
|
|
|
|
contract.AssertNoErrorf(def.ImportLanguages(map[string]schema.Language{"go": Importer}),
|
2023-02-08 19:18:48 +00:00
|
|
|
"Could not import languages")
|
2023-11-04 12:17:41 +00:00
|
|
|
if info, ok := def.Language["go"].(GoPackageInfo); ok {
|
|
|
|
return info
|
2021-09-30 18:56:42 +00:00
|
|
|
}
|
2023-11-04 12:17:41 +00:00
|
|
|
return GoPackageInfo{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NeedsGoOutputVersion(f *schema.Function) bool {
|
|
|
|
goInfo := goPackageInfo(f.PackageReference)
|
2021-09-30 18:56:42 +00:00
|
|
|
|
|
|
|
if goInfo.DisableFunctionOutputVersions {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
return f.ReturnType != nil
|
2021-09-30 18:56:42 +00:00
|
|
|
}
|
|
|
|
|
2021-11-23 23:10:15 +00:00
|
|
|
func (pkg *pkgContext) genFunctionCodeFile(f *schema.Function) (string, error) {
|
2021-08-23 20:46:09 +00:00
|
|
|
importsAndAliases := map[string]string{}
|
|
|
|
pkg.getImports(f, importsAndAliases)
|
2021-12-10 23:35:24 +00:00
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = ""
|
2023-08-22 17:16:43 +00:00
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, pkg.internalModuleName)] = ""
|
2021-08-23 20:46:09 +00:00
|
|
|
buffer := &bytes.Buffer{}
|
2023-11-04 12:17:41 +00:00
|
|
|
goInfo := goPackageInfo(pkg.pkg)
|
2021-08-23 20:46:09 +00:00
|
|
|
var imports []string
|
2023-10-02 12:53:06 +00:00
|
|
|
if f.ReturnType != nil {
|
2021-08-23 20:46:09 +00:00
|
|
|
imports = []string{"context", "reflect"}
|
2023-11-04 12:17:41 +00:00
|
|
|
if goInfo.Generics == GenericsSettingSideBySide {
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(buffer, imports, importsAndAliases, false /* isUtil */)
|
2023-09-19 10:28:50 +00:00
|
|
|
emitGenericVariant := false
|
|
|
|
if err := pkg.genFunction(buffer, f, emitGenericVariant); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
pkg.genFunctionOutputVersion(buffer, f, emitGenericVariant)
|
|
|
|
return buffer.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) genGenericVariantFunctionCodeFile(f *schema.Function) (string, error) {
|
|
|
|
importsAndAliases := map[string]string{}
|
|
|
|
pkg.getImports(f, importsAndAliases)
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = ""
|
|
|
|
if f.NeedsOutputVersion() {
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, pkg.internalModuleName)] = ""
|
|
|
|
buffer := &bytes.Buffer{}
|
|
|
|
|
|
|
|
var imports []string
|
|
|
|
if NeedsGoOutputVersion(f) {
|
|
|
|
imports = []string{"context", "reflect"}
|
|
|
|
}
|
|
|
|
|
|
|
|
pkg.genHeader(buffer, imports, importsAndAliases, false /* isUtil */)
|
|
|
|
useGenericTypes := true
|
|
|
|
if err := pkg.genFunction(buffer, f, useGenericTypes); err != nil {
|
2021-11-23 23:10:15 +00:00
|
|
|
return "", err
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.genFunctionOutputVersion(buffer, f, useGenericTypes)
|
2021-11-23 23:10:15 +00:00
|
|
|
return buffer.String(), nil
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genFunction(w io.Writer, f *schema.Function, useGenericTypes bool) error {
|
2021-08-23 20:46:09 +00:00
|
|
|
name := pkg.functionName(f)
|
2023-01-11 22:17:14 +00:00
|
|
|
|
|
|
|
if f.MultiArgumentInputs {
|
|
|
|
return fmt.Errorf("go SDK-gen does not implement MultiArgumentInputs for function '%s'", f.Token)
|
|
|
|
}
|
|
|
|
|
|
|
|
var objectReturnType *schema.ObjectType
|
|
|
|
if f.ReturnType != nil {
|
|
|
|
if objectType, ok := f.ReturnType.(*schema.ObjectType); ok {
|
|
|
|
objectReturnType = objectType
|
|
|
|
} else {
|
|
|
|
// TODO: remove when we add support for generalized return type for go
|
|
|
|
return fmt.Errorf("go sdk-gen doesn't support non-Object return types for function %s", f.Token)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, f.Comment, f.DeprecationMessage, false)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
// Now, emit the function signature.
|
|
|
|
argsig := "ctx *pulumi.Context"
|
|
|
|
if f.Inputs != nil {
|
|
|
|
argsig = fmt.Sprintf("%s, args *%sArgs", argsig, name)
|
|
|
|
}
|
|
|
|
var retty string
|
2023-01-11 22:17:14 +00:00
|
|
|
if objectReturnType == nil {
|
2020-01-21 22:45:48 +00:00
|
|
|
retty = "error"
|
|
|
|
} else {
|
|
|
|
retty = fmt.Sprintf("(*%sResult, error)", name)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "func %s(%s, opts ...pulumi.InvokeOption) %s {\n", name, argsig, retty)
|
|
|
|
|
|
|
|
// Make a map of inputs to pass to the runtime function.
|
|
|
|
var inputsVar string
|
|
|
|
if f.Inputs == nil {
|
|
|
|
inputsVar = "nil"
|
2021-11-23 23:10:15 +00:00
|
|
|
} else if codegen.IsProvideDefaultsFuncRequired(f.Inputs) && !pkg.disableObjectDefaults {
|
|
|
|
inputsVar = "args.Defaults()"
|
2020-01-21 22:45:48 +00:00
|
|
|
} else {
|
|
|
|
inputsVar = "args"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now simply invoke the runtime function with the arguments.
|
|
|
|
var outputsType string
|
2023-01-11 22:17:14 +00:00
|
|
|
if objectReturnType == nil {
|
2020-01-21 22:45:48 +00:00
|
|
|
outputsType = "struct{}"
|
|
|
|
} else {
|
|
|
|
outputsType = name + "Result"
|
|
|
|
}
|
2022-01-11 01:38:54 +00:00
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
err := pkg.GenPkgDefaultsOptsCall(w, true /*invoke*/)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-01-11 01:38:54 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tvar rv %s\n", outputsType)
|
|
|
|
fmt.Fprintf(w, "\terr := ctx.Invoke(\"%s\", %s, &rv, opts...)\n", f.Token, inputsVar)
|
|
|
|
|
2023-01-11 22:17:14 +00:00
|
|
|
if objectReturnType == nil {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\treturn err\n")
|
|
|
|
} else {
|
|
|
|
// Check the error before proceeding.
|
|
|
|
fmt.Fprintf(w, "\tif err != nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\treturn nil, err\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
|
|
|
|
// Return the result.
|
2021-11-23 23:10:15 +00:00
|
|
|
var retValue string
|
2023-01-11 22:17:14 +00:00
|
|
|
if codegen.IsProvideDefaultsFuncRequired(objectReturnType) && !pkg.disableObjectDefaults {
|
2021-11-23 23:10:15 +00:00
|
|
|
retValue = "rv.Defaults()"
|
|
|
|
} else {
|
|
|
|
retValue = "&rv"
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\treturn %s, nil\n", retValue)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
|
|
|
// If there are argument and/or return types, emit them.
|
|
|
|
if f.Inputs != nil {
|
|
|
|
fmt.Fprintf(w, "\n")
|
2021-11-23 23:10:15 +00:00
|
|
|
fnInputsName := pkg.functionArgsTypeName(f)
|
|
|
|
pkg.genPlainType(w, fnInputsName, f.Inputs.Comment, "", f.Inputs.Properties)
|
|
|
|
if codegen.IsProvideDefaultsFuncRequired(f.Inputs) && !pkg.disableObjectDefaults {
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genObjectDefaultFunc(w, fnInputsName, f.Inputs.Properties, useGenericTypes); err != nil {
|
2021-11-23 23:10:15 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
if objectReturnType != nil {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\n")
|
2021-11-23 23:10:15 +00:00
|
|
|
fnOutputsName := pkg.functionResultTypeName(f)
|
2023-01-11 22:17:14 +00:00
|
|
|
pkg.genPlainType(w, fnOutputsName, objectReturnType.Comment, "", objectReturnType.Properties)
|
|
|
|
if codegen.IsProvideDefaultsFuncRequired(objectReturnType) && !pkg.disableObjectDefaults {
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genObjectDefaultFunc(w, fnOutputsName, objectReturnType.Properties, useGenericTypes); err != nil {
|
2021-11-23 23:10:15 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
2021-11-23 23:10:15 +00:00
|
|
|
return nil
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) functionName(f *schema.Function) string {
|
|
|
|
// If the function starts with New or Get, it will conflict; so rename them.
|
|
|
|
name, hasName := pkg.functionNames[f]
|
|
|
|
|
|
|
|
if !hasName {
|
|
|
|
panic(fmt.Sprintf("No function name found for %v", f))
|
|
|
|
}
|
|
|
|
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) functionArgsTypeName(f *schema.Function) string {
|
|
|
|
name := pkg.functionName(f)
|
2023-12-12 12:19:42 +00:00
|
|
|
return name + "Args"
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) functionResultTypeName(f *schema.Function) string {
|
|
|
|
name := pkg.functionName(f)
|
2023-12-12 12:19:42 +00:00
|
|
|
return name + "Result"
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func genericTypeNeedsExplicitCasting(outputType string) bool {
|
|
|
|
return strings.HasPrefix(outputType, "pulumix.ArrayOutput") ||
|
|
|
|
strings.HasPrefix(outputType, "pulumix.MapOutput") ||
|
|
|
|
strings.HasPrefix(outputType, "pulumix.GPtrOutput") ||
|
|
|
|
strings.HasPrefix(outputType, "pulumix.GArrayOutput") ||
|
|
|
|
strings.HasPrefix(outputType, "pulumix.GMapOutput")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) genFunctionOutputGenericVersion(w io.Writer, f *schema.Function) {
|
|
|
|
originalName := pkg.functionName(f)
|
|
|
|
name := originalName + "Output"
|
|
|
|
originalResultTypeName := pkg.functionResultTypeName(f)
|
2023-12-12 12:19:42 +00:00
|
|
|
resultTypeName := originalResultTypeName + "Output"
|
2023-09-19 10:28:50 +00:00
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
code := ""
|
|
|
|
|
|
|
|
if f.Inputs != nil {
|
|
|
|
code = `
|
2023-09-19 10:28:50 +00:00
|
|
|
func ${fn}Output(ctx *pulumi.Context, args ${fn}OutputArgs, opts ...pulumi.InvokeOption) ${outputType} {
|
|
|
|
outputResult := pulumix.ApplyErr[*${fn}Args](args.ToOutput(), func(plainArgs *${fn}Args) (*${fn}Result, error) {
|
|
|
|
return ${fn}(ctx, plainArgs, opts...)
|
|
|
|
})
|
|
|
|
|
|
|
|
return pulumix.Cast[${outputType}, *${fn}Result](outputResult)
|
|
|
|
}
|
|
|
|
`
|
2023-10-02 12:53:06 +00:00
|
|
|
} else {
|
|
|
|
code = `
|
|
|
|
func ${fn}Output(ctx *pulumi.Context, opts ...pulumi.InvokeOption) ${outputType} {
|
|
|
|
outputResult := pulumix.ApplyErr[int](pulumix.Val(0), func(_ int) (*${fn}Result, error) {
|
|
|
|
return ${fn}(ctx, opts...)
|
|
|
|
})
|
|
|
|
|
|
|
|
return pulumix.Cast[${outputType}, *${fn}Result](outputResult)
|
|
|
|
}
|
|
|
|
`
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
|
|
|
|
code = strings.ReplaceAll(code, "${fn}", originalName)
|
|
|
|
code = strings.ReplaceAll(code, "${outputType}", resultTypeName)
|
|
|
|
fmt.Fprint(w, code)
|
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
if f.Inputs != nil {
|
|
|
|
useGenericTypes := true
|
|
|
|
pkg.genInputArgsStruct(w, name+"Args", f.Inputs.InputShape, useGenericTypes)
|
|
|
|
|
|
|
|
receiverType := name + "Args"
|
|
|
|
plainType := originalName + "Args"
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (args %s) ToOutput() pulumix.Output[*%s] {\n", receiverType, plainType)
|
|
|
|
fmt.Fprint(w, "\tallArgs := pulumix.All(\n")
|
|
|
|
for i, p := range f.Inputs.Properties {
|
|
|
|
fmt.Fprintf(w, "\t\targs.%s.ToOutput(context.Background()).AsAny()", pkg.fieldName(nil, p))
|
|
|
|
if i < len(f.Inputs.Properties)-1 {
|
|
|
|
fmt.Fprint(w, ",\n")
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
}
|
2023-10-02 12:53:06 +00:00
|
|
|
fmt.Fprint(w, ")\n")
|
2023-09-19 10:28:50 +00:00
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Apply[[]any](allArgs, func(resolvedArgs []interface{}) *%s {\n", plainType)
|
|
|
|
fmt.Fprintf(w, "\t\treturn &%s{\n", plainType)
|
|
|
|
for i, p := range f.Inputs.Properties {
|
|
|
|
fmt.Fprintf(w, "\t\t\t%s: resolvedArgs[%d].(%s),\n",
|
|
|
|
pkg.fieldName(nil, p),
|
|
|
|
i,
|
|
|
|
pkg.typeString(p.Type))
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t\t}\n")
|
|
|
|
fmt.Fprintf(w, "\t})\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
2023-09-19 10:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var objectReturnType *schema.ObjectType
|
|
|
|
if f.ReturnType != nil {
|
|
|
|
if objectType, ok := f.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
|
|
|
objectReturnType = objectType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if objectReturnType != nil {
|
|
|
|
fmt.Fprintf(w, "type %sOutput struct { *pulumi.OutputState }\n\n", originalResultTypeName)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (%sOutput) ElementType() reflect.Type {\n", originalResultTypeName)
|
|
|
|
fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s)(nil)).Elem()\n", originalResultTypeName)
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %sOutput) ToOutput(context.Context) pulumix.Output[*%s] {\n",
|
|
|
|
originalResultTypeName,
|
|
|
|
originalResultTypeName)
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Output[*%s]{\n", originalResultTypeName)
|
|
|
|
fmt.Fprint(w, "\t\tOutputState: o.OutputState,\n")
|
|
|
|
fmt.Fprint(w, "\t}\n")
|
|
|
|
fmt.Fprint(w, "}\n\n")
|
|
|
|
|
|
|
|
// generate accessors for each property of the output
|
|
|
|
for _, p := range objectReturnType.Properties {
|
|
|
|
outputType := pkg.genericOutputType(p.Type)
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (o %s) %s() %s {\n", resultTypeName, pkg.fieldName(nil, p), outputType)
|
|
|
|
|
|
|
|
needsCast := genericTypeNeedsExplicitCasting(outputType)
|
|
|
|
|
|
|
|
if !needsCast {
|
|
|
|
fmt.Fprintf(w, "\treturn pulumix.Apply[*%s](o, func (v *%s) %s { return v.%s })\n",
|
|
|
|
originalResultTypeName,
|
|
|
|
originalResultTypeName,
|
|
|
|
pkg.typeString(p.Type),
|
|
|
|
pkg.fieldName(nil, p))
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\tvalue := pulumix.Apply[*%s](o, func (v *%s) %s { return v.%s })\n",
|
|
|
|
originalResultTypeName,
|
|
|
|
originalResultTypeName,
|
|
|
|
pkg.typeString(p.Type),
|
|
|
|
pkg.fieldName(nil, p))
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "\treturn %s{\n", outputType)
|
|
|
|
fmt.Fprintf(w, "\t\tOutputState: value.OutputState,\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) genFunctionOutputVersion(w io.Writer, f *schema.Function, useGenericTypes bool) {
|
2023-10-02 12:53:06 +00:00
|
|
|
if f.ReturnType == nil {
|
2021-08-23 20:46:09 +00:00
|
|
|
return
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericTypes {
|
|
|
|
pkg.genFunctionOutputGenericVersion(w, f)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-23 20:46:09 +00:00
|
|
|
originalName := pkg.functionName(f)
|
|
|
|
name := originalName + "Output"
|
|
|
|
originalResultTypeName := pkg.functionResultTypeName(f)
|
|
|
|
resultTypeName := originalResultTypeName + "Output"
|
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
code := ""
|
|
|
|
|
|
|
|
if f.Inputs != nil {
|
|
|
|
code = `
|
2021-08-23 20:46:09 +00:00
|
|
|
func ${fn}Output(ctx *pulumi.Context, args ${fn}OutputArgs, opts ...pulumi.InvokeOption) ${outputType} {
|
|
|
|
return pulumi.ToOutputWithContext(context.Background(), args).
|
|
|
|
ApplyT(func(v interface{}) (${fn}Result, error) {
|
|
|
|
args := v.(${fn}Args)
|
|
|
|
r, err := ${fn}(ctx, &args, opts...)
|
[codegen/go] Update Go SDK function output to check for errors (#9274)
* [codegen/go] Update Go SDK function output to check for errors
Fixes https://github.com/pulumi/pulumi-aws/issues/1872.
This should result in the following sample output in the Go SDK:
```
func GetPolicyDocumentOutput(ctx *pulumi.Context, args GetPolicyDocumentOutputArgs, opts ...pulumi.InvokeOption) GetPolicyDocumentResultOutput {
return pulumi.ToOutputWithContext(context.Background(), args).
ApplyT(func(v interface{}) (GetPolicyDocumentResult, error) {
args := v.(GetPolicyDocumentArgs)
r, err := GetPolicyDocument(ctx, &args, opts...)
if err != nil {
return nil, err
}
if r == nil {
return nil, fmt.Errorf("expected either result or error to be nil, not both")
}
return *r, err
}).(GetPolicyDocumentResultOutput)
}
```
* Fix generated code panic on *nil (#9284)
* [codegen/go] Update Go SDK function output to check for errors
Fixes https://github.com/pulumi/pulumi-aws/issues/1872.
This should result in the following sample output in the Go SDK:
```
func GetPolicyDocumentOutput(ctx *pulumi.Context, args GetPolicyDocumentOutputArgs, opts ...pulumi.InvokeOption) GetPolicyDocumentResultOutput {
return pulumi.ToOutputWithContext(context.Background(), args).
ApplyT(func(v interface{}) (GetPolicyDocumentResult, error) {
args := v.(GetPolicyDocumentArgs)
r, err := GetPolicyDocument(ctx, &args, opts...)
if r != nil {
s = *r
}
return s, err
}).(GetPolicyDocumentResultOutput)
}
```
* Alternate fix to safeguard dereferencing nil
* Accept codegen changes in the test suite
Co-authored-by: Guinevere Saenger <guinevere@pulumi.com>
* Update CHANGELOG_PENDING.md
Co-authored-by: Anton Tayanovskyy <anton@pulumi.com>
2022-03-25 03:11:53 +00:00
|
|
|
var s ${fn}Result
|
|
|
|
if r != nil {
|
|
|
|
s = *r
|
|
|
|
}
|
|
|
|
return s, err
|
2021-08-23 20:46:09 +00:00
|
|
|
}).(${outputType})
|
|
|
|
}
|
|
|
|
|
|
|
|
`
|
2023-10-02 12:53:06 +00:00
|
|
|
} else {
|
|
|
|
code = `
|
|
|
|
func ${fn}Output(ctx *pulumi.Context, opts ...pulumi.InvokeOption) ${outputType} {
|
|
|
|
return pulumi.ToOutput(0).ApplyT(func(int) (${fn}Result, error) {
|
|
|
|
r, err := ${fn}(ctx, opts...)
|
|
|
|
var s ${fn}Result
|
|
|
|
if r != nil {
|
|
|
|
s = *r
|
|
|
|
}
|
|
|
|
return s, err
|
|
|
|
}).(${outputType})
|
|
|
|
}
|
|
|
|
|
|
|
|
`
|
|
|
|
}
|
[codegen/go] Update Go SDK function output to check for errors (#9274)
* [codegen/go] Update Go SDK function output to check for errors
Fixes https://github.com/pulumi/pulumi-aws/issues/1872.
This should result in the following sample output in the Go SDK:
```
func GetPolicyDocumentOutput(ctx *pulumi.Context, args GetPolicyDocumentOutputArgs, opts ...pulumi.InvokeOption) GetPolicyDocumentResultOutput {
return pulumi.ToOutputWithContext(context.Background(), args).
ApplyT(func(v interface{}) (GetPolicyDocumentResult, error) {
args := v.(GetPolicyDocumentArgs)
r, err := GetPolicyDocument(ctx, &args, opts...)
if err != nil {
return nil, err
}
if r == nil {
return nil, fmt.Errorf("expected either result or error to be nil, not both")
}
return *r, err
}).(GetPolicyDocumentResultOutput)
}
```
* Fix generated code panic on *nil (#9284)
* [codegen/go] Update Go SDK function output to check for errors
Fixes https://github.com/pulumi/pulumi-aws/issues/1872.
This should result in the following sample output in the Go SDK:
```
func GetPolicyDocumentOutput(ctx *pulumi.Context, args GetPolicyDocumentOutputArgs, opts ...pulumi.InvokeOption) GetPolicyDocumentResultOutput {
return pulumi.ToOutputWithContext(context.Background(), args).
ApplyT(func(v interface{}) (GetPolicyDocumentResult, error) {
args := v.(GetPolicyDocumentArgs)
r, err := GetPolicyDocument(ctx, &args, opts...)
if r != nil {
s = *r
}
return s, err
}).(GetPolicyDocumentResultOutput)
}
```
* Alternate fix to safeguard dereferencing nil
* Accept codegen changes in the test suite
Co-authored-by: Guinevere Saenger <guinevere@pulumi.com>
* Update CHANGELOG_PENDING.md
Co-authored-by: Anton Tayanovskyy <anton@pulumi.com>
2022-03-25 03:11:53 +00:00
|
|
|
|
2021-08-23 20:46:09 +00:00
|
|
|
code = strings.ReplaceAll(code, "${fn}", originalName)
|
|
|
|
code = strings.ReplaceAll(code, "${outputType}", resultTypeName)
|
2023-01-13 18:56:53 +00:00
|
|
|
fmt.Fprint(w, code)
|
2021-08-23 20:46:09 +00:00
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
if f.Inputs != nil {
|
|
|
|
pkg.genInputArgsStruct(w, name+"Args", f.Inputs.InputShape, false /*emitGenericVariant*/)
|
2021-08-23 20:46:09 +00:00
|
|
|
|
2023-10-02 12:53:06 +00:00
|
|
|
genInputImplementationWithArgs(w, genInputImplementationArgs{
|
|
|
|
name: name + "Args",
|
|
|
|
receiverType: name + "Args",
|
|
|
|
elementType: pkg.functionArgsTypeName(f),
|
|
|
|
usingGenericTypes: useGenericTypes,
|
|
|
|
})
|
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
if f.ReturnType != nil {
|
|
|
|
if objectType, ok := f.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
|
|
|
pkg.genOutputTypes(w, genOutputTypesArgs{
|
2023-10-02 12:53:06 +00:00
|
|
|
t: objectType,
|
|
|
|
name: originalResultTypeName,
|
|
|
|
output: true,
|
2023-01-11 22:17:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
|
|
|
|
// Assuming the file represented by `w` only has one function,
|
|
|
|
// generate an `init()` for Output type init.
|
|
|
|
initCode := `
|
|
|
|
func init() {
|
|
|
|
pulumi.RegisterOutputType(${outputType}{})
|
|
|
|
}
|
|
|
|
|
|
|
|
`
|
|
|
|
initCode = strings.ReplaceAll(initCode, "${outputType}", resultTypeName)
|
2023-01-13 18:56:53 +00:00
|
|
|
fmt.Fprint(w, initCode)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 14:33:14 +00:00
|
|
|
type objectProperty struct {
|
|
|
|
object *schema.ObjectType
|
|
|
|
property *schema.Property
|
|
|
|
}
|
|
|
|
|
|
|
|
// When computing the type name for a field of an object type, we must ensure that we do not generate invalid recursive
|
|
|
|
// struct types. A struct type T contains invalid recursion if the closure of its fields and its struct-typed fields'
|
|
|
|
// fields includes a field of type T. A few examples:
|
|
|
|
//
|
|
|
|
// Directly invalid:
|
|
|
|
//
|
2022-09-14 02:12:02 +00:00
|
|
|
// type T struct {
|
|
|
|
// Invalid T
|
|
|
|
// }
|
2021-09-28 14:33:14 +00:00
|
|
|
//
|
|
|
|
// Indirectly invalid:
|
|
|
|
//
|
2022-09-14 02:12:02 +00:00
|
|
|
// type T struct {
|
|
|
|
// Invalid S
|
|
|
|
// }
|
2021-09-28 14:33:14 +00:00
|
|
|
//
|
2022-09-14 02:12:02 +00:00
|
|
|
// type S struct {
|
|
|
|
// Invalid T
|
|
|
|
// }
|
2021-09-28 14:33:14 +00:00
|
|
|
//
|
|
|
|
// In order to avoid generating invalid struct types, we replace all references to types involved in a cyclical
|
|
|
|
// definition with *T. The examples above therefore become:
|
|
|
|
//
|
|
|
|
// (1)
|
2022-09-14 02:12:02 +00:00
|
|
|
//
|
|
|
|
// type T struct {
|
|
|
|
// Valid *T
|
|
|
|
// }
|
2021-09-28 14:33:14 +00:00
|
|
|
//
|
|
|
|
// (2)
|
|
|
|
//
|
2022-09-14 02:12:02 +00:00
|
|
|
// type T struct {
|
|
|
|
// Valid *S
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// type S struct {
|
|
|
|
// Valid *T
|
|
|
|
// }
|
2021-09-28 14:33:14 +00:00
|
|
|
//
|
|
|
|
// We do this using a rewriter that turns all fields involved in reference cycles into optional fields.
|
|
|
|
func rewriteCyclicField(rewritten codegen.Set, path []objectProperty, op objectProperty) {
|
|
|
|
// If this property refers to an Input<> type, unwrap the type. This ensures that the plain and input shapes of an
|
|
|
|
// object type remain identical.
|
|
|
|
t := op.property.Type
|
|
|
|
if inputType, isInputType := op.property.Type.(*schema.InputType); isInputType {
|
|
|
|
t = inputType.ElementType
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this property does not refer to an object type, it cannot be involved in a cycle. Skip it.
|
|
|
|
objectType, isObjectType := t.(*schema.ObjectType)
|
|
|
|
if !isObjectType {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
path = append(path, op)
|
|
|
|
|
|
|
|
// Check the current path for cycles by crawling backwards until reaching the start of the path
|
|
|
|
// or finding a property that is a member of the current object type.
|
|
|
|
var cycle []objectProperty
|
|
|
|
for i := len(path) - 1; i > 0; i-- {
|
|
|
|
if path[i].object == objectType {
|
|
|
|
cycle = path[i:]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the current path does not involve a cycle, recur into the current object type.
|
|
|
|
if len(cycle) == 0 {
|
|
|
|
rewriteCyclicFields(rewritten, path, objectType)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we've found a cycle, mark each property involved in the cycle as optional.
|
|
|
|
//
|
|
|
|
// NOTE: this overestimates the set of properties that must be marked as optional. For example, in case (2) above,
|
|
|
|
// only one of T.Invalid or S.Invalid needs to be marked as optional in order to break the cycle. However, choosing
|
|
|
|
// a minimal set of properties that is also deterministic and resilient to changes in visit order is difficult and
|
|
|
|
// seems to add little value.
|
|
|
|
for _, p := range cycle {
|
|
|
|
p.property.Type = codegen.OptionalType(p.property)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func rewriteCyclicFields(rewritten codegen.Set, path []objectProperty, obj *schema.ObjectType) {
|
|
|
|
if !rewritten.Has(obj) {
|
|
|
|
rewritten.Add(obj)
|
|
|
|
for _, property := range obj.Properties {
|
|
|
|
rewriteCyclicField(rewritten, path, objectProperty{obj, property})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func rewriteCyclicObjectFields(pkg *schema.Package) {
|
|
|
|
rewritten := codegen.Set{}
|
|
|
|
for _, t := range pkg.Types {
|
|
|
|
if obj, ok := t.(*schema.ObjectType); ok && !obj.IsInputShape() {
|
|
|
|
rewriteCyclicFields(rewritten, nil, obj)
|
|
|
|
rewriteCyclicFields(rewritten, nil, obj.InputShape)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genType(w io.Writer, obj *schema.ObjectType, usingGenericTypes bool) error {
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(!obj.IsInputShape(), "Object type must not have input shape")
|
2021-11-12 00:00:03 +00:00
|
|
|
if obj.IsOverlay {
|
|
|
|
// This type is generated by the provider, so no further action is required.
|
2021-11-23 23:10:15 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
plainName := pkg.tokenToType(obj.Token)
|
2023-11-21 16:43:51 +00:00
|
|
|
if !usingGenericTypes {
|
|
|
|
pkg.genPlainType(w, plainName, obj.Comment, "", obj.Properties)
|
|
|
|
} else {
|
|
|
|
pkg.genGenericPlainType(w, plainName, obj.Comment, "", obj.Properties)
|
|
|
|
}
|
|
|
|
|
2021-11-23 23:10:15 +00:00
|
|
|
if !pkg.disableObjectDefaults {
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genObjectDefaultFunc(w, plainName, obj.Properties, usingGenericTypes); err != nil {
|
2021-11-23 23:10:15 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-11-12 00:00:03 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genInputTypes(w, obj.InputShape, pkg.detailsForType(obj), usingGenericTypes); err != nil {
|
2022-05-03 18:13:21 +00:00
|
|
|
return err
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.genOutputTypes(w, genOutputTypesArgs{
|
|
|
|
t: obj,
|
|
|
|
usingGenericTypes: usingGenericTypes,
|
|
|
|
})
|
2021-11-23 23:10:15 +00:00
|
|
|
return nil
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
func (pkg *pkgContext) addSuffixesToName(typ schema.Type, name string) []string {
|
|
|
|
var names []string
|
|
|
|
details := pkg.detailsForType(typ)
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.arrayInput {
|
|
|
|
names = append(names, name+"ArrayInput")
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.arrayOutput || details.arrayInput {
|
|
|
|
names = append(names, name+"ArrayOutput")
|
|
|
|
}
|
|
|
|
if details.mapInput {
|
|
|
|
names = append(names, name+"MapInput")
|
|
|
|
}
|
|
|
|
if details.mapOutput || details.mapInput {
|
|
|
|
names = append(names, name+"MapOutput")
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
2021-12-03 22:10:29 +00:00
|
|
|
type nestedTypeInfo struct {
|
|
|
|
resolvedElementType string
|
|
|
|
names map[string]bool
|
|
|
|
}
|
|
|
|
|
2021-08-18 17:04:07 +00:00
|
|
|
// collectNestedCollectionTypes builds a deduped mapping of element types -> associated collection types.
|
|
|
|
// different shapes of known types can resolve to the same element type. by collecting types in one step and emitting types
|
|
|
|
// in a second step, we avoid collision and redeclaration.
|
2021-12-03 22:10:29 +00:00
|
|
|
func (pkg *pkgContext) collectNestedCollectionTypes(types map[string]*nestedTypeInfo, typ schema.Type) {
|
2021-03-11 05:04:30 +00:00
|
|
|
var elementTypeName string
|
|
|
|
var names []string
|
|
|
|
switch t := typ.(type) {
|
|
|
|
case *schema.ArrayType:
|
2021-03-15 21:44:11 +00:00
|
|
|
// Builtins already cater to primitive arrays
|
|
|
|
if schema.IsPrimitiveType(t.ElementType) {
|
2021-08-18 17:04:07 +00:00
|
|
|
return
|
2021-03-15 21:44:11 +00:00
|
|
|
}
|
2021-03-15 06:44:21 +00:00
|
|
|
elementTypeName = pkg.nestedTypeToType(t.ElementType)
|
2021-08-18 17:04:07 +00:00
|
|
|
elementTypeName = strings.TrimSuffix(elementTypeName, "Args") + "Array"
|
2022-06-21 18:04:13 +00:00
|
|
|
|
|
|
|
// We make sure that subsidiary elements are marked for array as well
|
|
|
|
details := pkg.detailsForType(t)
|
|
|
|
pkg.detailsForType(t.ElementType).markArray(details.arrayInput, details.arrayOutput)
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
names = pkg.addSuffixesToName(t, elementTypeName)
|
2022-06-21 18:04:13 +00:00
|
|
|
defer pkg.collectNestedCollectionTypes(types, t.ElementType)
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.MapType:
|
2021-03-15 21:44:11 +00:00
|
|
|
// Builtins already cater to primitive maps
|
|
|
|
if schema.IsPrimitiveType(t.ElementType) {
|
2021-08-18 17:04:07 +00:00
|
|
|
return
|
2021-03-15 21:44:11 +00:00
|
|
|
}
|
2021-03-15 06:44:21 +00:00
|
|
|
elementTypeName = pkg.nestedTypeToType(t.ElementType)
|
2021-08-18 17:04:07 +00:00
|
|
|
elementTypeName = strings.TrimSuffix(elementTypeName, "Args") + "Map"
|
2022-06-21 18:04:13 +00:00
|
|
|
|
|
|
|
// We make sure that subsidiary elements are marked for map as well
|
|
|
|
details := pkg.detailsForType(t)
|
|
|
|
pkg.detailsForType(t.ElementType).markMap(details.mapInput, details.mapOutput)
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
names = pkg.addSuffixesToName(t, elementTypeName)
|
2022-06-21 18:04:13 +00:00
|
|
|
defer pkg.collectNestedCollectionTypes(types, t.ElementType)
|
2021-06-24 16:17:55 +00:00
|
|
|
default:
|
2022-06-21 18:04:13 +00:00
|
|
|
return
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
2021-12-03 22:10:29 +00:00
|
|
|
nti, ok := types[elementTypeName]
|
|
|
|
if !ok {
|
|
|
|
nti = &nestedTypeInfo{
|
|
|
|
names: map[string]bool{},
|
|
|
|
resolvedElementType: pkg.typeString(codegen.ResolvedType(typ)),
|
|
|
|
}
|
|
|
|
types[elementTypeName] = nti
|
2021-08-18 17:04:07 +00:00
|
|
|
}
|
|
|
|
for _, n := range names {
|
2021-12-03 22:10:29 +00:00
|
|
|
nti.names[n] = true
|
2021-08-18 17:04:07 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-11 05:04:30 +00:00
|
|
|
|
2021-08-18 17:04:07 +00:00
|
|
|
// genNestedCollectionTypes emits nested collection types given the deduped mapping of element types -> associated collection types.
|
|
|
|
// different shapes of known types can resolve to the same element type. by collecting types in one step and emitting types
|
|
|
|
// in a second step, we avoid collision and redeclaration.
|
2021-12-03 22:10:29 +00:00
|
|
|
func (pkg *pkgContext) genNestedCollectionTypes(w io.Writer, types map[string]*nestedTypeInfo) []string {
|
2021-08-18 17:04:07 +00:00
|
|
|
var names []string
|
2021-08-18 19:07:30 +00:00
|
|
|
|
|
|
|
// map iteration is unstable so sort items for deterministic codegen
|
|
|
|
sortedElems := []string{}
|
|
|
|
for k := range types {
|
|
|
|
sortedElems = append(sortedElems, k)
|
|
|
|
}
|
|
|
|
sort.Strings(sortedElems)
|
|
|
|
|
|
|
|
for _, elementTypeName := range sortedElems {
|
2021-12-03 22:10:29 +00:00
|
|
|
info := types[elementTypeName]
|
|
|
|
|
2021-08-18 19:07:30 +00:00
|
|
|
collectionTypes := []string{}
|
2021-12-03 22:10:29 +00:00
|
|
|
for k := range info.names {
|
2021-08-18 19:07:30 +00:00
|
|
|
collectionTypes = append(collectionTypes, k)
|
|
|
|
}
|
|
|
|
sort.Strings(collectionTypes)
|
|
|
|
for _, name := range collectionTypes {
|
2021-08-18 17:04:07 +00:00
|
|
|
names = append(names, name)
|
2021-12-10 23:35:24 +00:00
|
|
|
switch {
|
|
|
|
case strings.HasSuffix(name, "ArrayInput"):
|
|
|
|
name = strings.TrimSuffix(name, "Input")
|
2021-08-18 17:04:07 +00:00
|
|
|
fmt.Fprintf(w, "type %s []%sInput\n\n", name, elementTypeName)
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name, name, "[]"+info.resolvedElementType, false, false)
|
2021-08-18 17:04:07 +00:00
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
pkg.genInputInterface(w, name)
|
|
|
|
case strings.HasSuffix(name, "ArrayOutput"):
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genArrayOutput(w, strings.TrimSuffix(name, "ArrayOutput"), info.resolvedElementType)
|
2021-12-10 23:35:24 +00:00
|
|
|
case strings.HasSuffix(name, "MapInput"):
|
|
|
|
name = strings.TrimSuffix(name, "Input")
|
2021-08-18 17:04:07 +00:00
|
|
|
fmt.Fprintf(w, "type %s map[string]%sInput\n\n", name, elementTypeName)
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genInputImplementation(w, name, name, "map[string]"+info.resolvedElementType, false, false)
|
2021-03-11 05:04:30 +00:00
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
pkg.genInputInterface(w, name)
|
|
|
|
case strings.HasSuffix(name, "MapOutput"):
|
2023-11-04 12:17:41 +00:00
|
|
|
pkg.genMapOutput(w, strings.TrimSuffix(name, "MapOutput"), info.resolvedElementType)
|
2021-08-18 17:04:07 +00:00
|
|
|
}
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
2021-03-15 06:44:21 +00:00
|
|
|
func (pkg *pkgContext) nestedTypeToType(typ schema.Type) string {
|
2021-06-24 16:17:55 +00:00
|
|
|
switch t := codegen.UnwrapType(typ).(type) {
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.ArrayType:
|
2022-06-21 18:04:13 +00:00
|
|
|
return pkg.nestedTypeToType(t.ElementType) + "Array"
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.MapType:
|
2022-06-21 18:04:13 +00:00
|
|
|
return pkg.nestedTypeToType(t.ElementType) + "Map"
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.ObjectType:
|
|
|
|
return pkg.resolveObjectType(t)
|
2023-12-05 12:29:30 +00:00
|
|
|
case *schema.EnumType:
|
|
|
|
return pkg.resolveEnumType(t)
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
2021-08-18 17:04:07 +00:00
|
|
|
return strings.TrimSuffix(pkg.tokenToType(typ.String()), "Args")
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genTypeRegistrations(
|
|
|
|
w io.Writer,
|
|
|
|
objTypes []*schema.ObjectType,
|
|
|
|
usingGenericTypes bool,
|
|
|
|
types ...string,
|
|
|
|
) {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "func init() {\n")
|
2021-10-07 22:26:24 +00:00
|
|
|
|
|
|
|
// Input types.
|
2023-09-19 10:28:50 +00:00
|
|
|
if !pkg.disableInputTypeRegistrations && !usingGenericTypes {
|
2021-10-12 17:15:24 +00:00
|
|
|
for _, obj := range objTypes {
|
2021-11-12 00:00:03 +00:00
|
|
|
if obj.IsOverlay {
|
|
|
|
// This type is generated by the provider, so no further action is required.
|
|
|
|
continue
|
|
|
|
}
|
2021-10-12 17:15:24 +00:00
|
|
|
name, details := pkg.tokenToType(obj.Token), pkg.detailsForType(obj)
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.input {
|
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sInput)(nil)).Elem(), %[1]sArgs{})\n", name)
|
|
|
|
}
|
|
|
|
if details.ptrInput {
|
2021-10-12 17:15:24 +00:00
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sPtrInput)(nil)).Elem(), %[1]sArgs{})\n", name)
|
|
|
|
}
|
2022-08-31 20:35:26 +00:00
|
|
|
if details.arrayInput && !pkg.names.Has(name+"Array") {
|
2021-10-12 17:15:24 +00:00
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sArrayInput)(nil)).Elem(), %[1]sArray{})\n", name)
|
|
|
|
}
|
2022-08-31 20:35:26 +00:00
|
|
|
if details.mapInput && !pkg.names.Has(name+"Map") {
|
2021-10-12 17:15:24 +00:00
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sMapInput)(nil)).Elem(), %[1]sMap{})\n", name)
|
|
|
|
}
|
2021-10-07 22:26:24 +00:00
|
|
|
}
|
2021-10-12 17:15:24 +00:00
|
|
|
for _, t := range types {
|
2021-12-10 23:35:24 +00:00
|
|
|
if strings.HasSuffix(t, "Input") {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterInputType(reflect.TypeOf((*%s)(nil)).Elem(), %s{})\n", t, strings.TrimSuffix(t, "Input"))
|
|
|
|
}
|
2021-10-07 22:26:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output types.
|
2021-03-11 05:04:30 +00:00
|
|
|
for _, obj := range objTypes {
|
2021-11-12 00:00:03 +00:00
|
|
|
if obj.IsOverlay {
|
|
|
|
// This type is generated by the provider, so no further action is required.
|
|
|
|
continue
|
|
|
|
}
|
2020-11-03 07:02:56 +00:00
|
|
|
name, details := pkg.tokenToType(obj.Token), pkg.detailsForType(obj)
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.output {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sOutput{})\n", name)
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.ptrOutput && !usingGenericTypes {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sPtrOutput{})\n", name)
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.arrayOutput && !usingGenericTypes {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sArrayOutput{})\n", name)
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
if details.mapOutput && !usingGenericTypes {
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sMapOutput{})\n", name)
|
|
|
|
}
|
|
|
|
}
|
2021-03-11 05:04:30 +00:00
|
|
|
for _, t := range types {
|
2021-12-10 23:35:24 +00:00
|
|
|
if strings.HasSuffix(t, "Output") {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%s{})\n", t)
|
|
|
|
}
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
2021-10-07 22:26:24 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
}
|
|
|
|
|
2021-10-14 17:41:40 +00:00
|
|
|
func (pkg *pkgContext) genEnumRegistrations(w io.Writer) {
|
|
|
|
fmt.Fprintf(w, "func init() {\n")
|
|
|
|
// Register all input types
|
|
|
|
if !pkg.disableInputTypeRegistrations {
|
|
|
|
for _, e := range pkg.enums {
|
|
|
|
// Enums are guaranteed to have at least one element when they are
|
|
|
|
// bound into a schema.
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(len(e.Elements) > 0, "Enum must have at least one element")
|
2021-10-14 17:41:40 +00:00
|
|
|
name, details := pkg.tokenToEnum(e.Token), pkg.detailsForType(e)
|
|
|
|
instance := fmt.Sprintf("%#v", e.Elements[0].Value)
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.input || details.ptrInput {
|
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sInput)(nil)).Elem(), %[1]s(%[2]s))\n",
|
|
|
|
name, instance)
|
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sPtrInput)(nil)).Elem(), %[1]s(%[2]s))\n",
|
|
|
|
name, instance)
|
|
|
|
}
|
|
|
|
if details.arrayInput {
|
2021-10-14 17:41:40 +00:00
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sArrayInput)(nil)).Elem(), %[1]sArray{})\n",
|
|
|
|
name)
|
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.mapInput {
|
2021-10-14 17:41:40 +00:00
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sMapInput)(nil)).Elem(), %[1]sMap{})\n",
|
|
|
|
name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Register all output types
|
|
|
|
for _, e := range pkg.enums {
|
|
|
|
name, details := pkg.tokenToEnum(e.Token), pkg.detailsForType(e)
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.output || details.ptrOutput {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sOutput{})\n", name)
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sPtrOutput{})\n", name)
|
|
|
|
}
|
|
|
|
if details.arrayOutput {
|
2021-10-14 17:41:40 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sArrayOutput{})\n", name)
|
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
if details.mapOutput {
|
2021-10-14 17:41:40 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sMapOutput{})\n", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func (pkg *pkgContext) genResourceRegistrations(
|
|
|
|
w io.Writer,
|
|
|
|
r *schema.Resource,
|
|
|
|
generateResourceContainerTypes bool,
|
|
|
|
usingGenericTypes bool,
|
|
|
|
) {
|
2021-10-14 17:41:40 +00:00
|
|
|
name := disambiguatedResourceName(r, pkg)
|
|
|
|
fmt.Fprintf(w, "func init() {\n")
|
|
|
|
// Register input type
|
2023-09-19 10:28:50 +00:00
|
|
|
if !pkg.disableInputTypeRegistrations && !usingGenericTypes {
|
2021-10-14 17:41:40 +00:00
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sInput)(nil)).Elem(), &%[1]s{})\n",
|
|
|
|
name)
|
[codegen/go] Remove ResourcePtr input/output types (#8449)
These changes remove the `Ptr` variants of input/ouptut types for
resources. A `TPtr` input or output is normally generated for `T` if `T`
is present in an `optional(input(T))` or `optional(output(T))` and if
the Go representation for `T` is not nilable. The generation of `Ptr`
variants for resource types breaks the latter rule: the canonical
representation of a resource type named `Foo` is a pointer to a struct
type named `Foo` (i.e. `*Foo`). `Foo` itself is not a resource, as it
does not implement the Go `Resource` interface. Because this
representation already accommodates `nil` to indicate the lack of a
value, we need not generate `FooPtr{Input,Output}` types.
Besides being unnecessary, the implementation of `Ptr` types for
resources was incorrect. Rather than using `**Foo` as their element
type, these types use `*Foo`--identical to the element type used for
the normal input/output types. Furthermore, the generated code for
at least `FooOutput.ToFooPtrOutputWithContext` and `FooPtrOutput.Elem`
was incorrect, making these types virtually unusable in practice.
Finally, these `Ptr` types should never appear on input/output
properties in practice, as the logic we use to generate input and output
type references never generates them for `optional({input,output}(T)).
Instead, it generates references to the standard input/output types.
Though this is _technically_ a breaking change--it changes the set of
exported types for any package that defines resources--I believe that in
practice it will be invisible to users for the reasons stated above.
These types are not usable, and were never referenced.
This is preparatory work for #7943.
2021-11-23 18:24:56 +00:00
|
|
|
if generateResourceContainerTypes && !r.IsProvider {
|
2021-10-14 17:41:40 +00:00
|
|
|
fmt.Fprintf(w,
|
[codegen/go] Remove ResourcePtr input/output types (#8449)
These changes remove the `Ptr` variants of input/ouptut types for
resources. A `TPtr` input or output is normally generated for `T` if `T`
is present in an `optional(input(T))` or `optional(output(T))` and if
the Go representation for `T` is not nilable. The generation of `Ptr`
variants for resource types breaks the latter rule: the canonical
representation of a resource type named `Foo` is a pointer to a struct
type named `Foo` (i.e. `*Foo`). `Foo` itself is not a resource, as it
does not implement the Go `Resource` interface. Because this
representation already accommodates `nil` to indicate the lack of a
value, we need not generate `FooPtr{Input,Output}` types.
Besides being unnecessary, the implementation of `Ptr` types for
resources was incorrect. Rather than using `**Foo` as their element
type, these types use `*Foo`--identical to the element type used for
the normal input/output types. Furthermore, the generated code for
at least `FooOutput.ToFooPtrOutputWithContext` and `FooPtrOutput.Elem`
was incorrect, making these types virtually unusable in practice.
Finally, these `Ptr` types should never appear on input/output
properties in practice, as the logic we use to generate input and output
type references never generates them for `optional({input,output}(T)).
Instead, it generates references to the standard input/output types.
Though this is _technically_ a breaking change--it changes the set of
exported types for any package that defines resources--I believe that in
practice it will be invisible to users for the reasons stated above.
These types are not usable, and were never referenced.
This is preparatory work for #7943.
2021-11-23 18:24:56 +00:00
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sArrayInput)(nil)).Elem(), %[1]sArray{})\n",
|
|
|
|
name)
|
|
|
|
fmt.Fprintf(w,
|
|
|
|
"\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sMapInput)(nil)).Elem(), %[1]sMap{})\n",
|
2021-10-14 17:41:40 +00:00
|
|
|
name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Register all output types
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sOutput{})\n", name)
|
|
|
|
for _, method := range r.Methods {
|
2023-01-11 22:17:14 +00:00
|
|
|
|
|
|
|
var objectReturnType *schema.ObjectType
|
|
|
|
if method.Function.ReturnType != nil {
|
|
|
|
if objectType, ok := method.Function.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
|
|
|
objectReturnType = objectType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if objectReturnType != nil {
|
|
|
|
if pkg.liftSingleValueMethodReturns && len(objectReturnType.Properties) == 1 {
|
2022-10-17 16:37:07 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%s%sResultOutput{})\n", cgstrings.Camel(name), Title(method.Name))
|
2021-10-14 17:41:40 +00:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%s%sResultOutput{})\n", name, Title(method.Name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if generateResourceContainerTypes && !r.IsProvider && !usingGenericTypes {
|
[codegen/go] Remove ResourcePtr input/output types (#8449)
These changes remove the `Ptr` variants of input/ouptut types for
resources. A `TPtr` input or output is normally generated for `T` if `T`
is present in an `optional(input(T))` or `optional(output(T))` and if
the Go representation for `T` is not nilable. The generation of `Ptr`
variants for resource types breaks the latter rule: the canonical
representation of a resource type named `Foo` is a pointer to a struct
type named `Foo` (i.e. `*Foo`). `Foo` itself is not a resource, as it
does not implement the Go `Resource` interface. Because this
representation already accommodates `nil` to indicate the lack of a
value, we need not generate `FooPtr{Input,Output}` types.
Besides being unnecessary, the implementation of `Ptr` types for
resources was incorrect. Rather than using `**Foo` as their element
type, these types use `*Foo`--identical to the element type used for
the normal input/output types. Furthermore, the generated code for
at least `FooOutput.ToFooPtrOutputWithContext` and `FooPtrOutput.Elem`
was incorrect, making these types virtually unusable in practice.
Finally, these `Ptr` types should never appear on input/output
properties in practice, as the logic we use to generate input and output
type references never generates them for `optional({input,output}(T)).
Instead, it generates references to the standard input/output types.
Though this is _technically_ a breaking change--it changes the set of
exported types for any package that defines resources--I believe that in
practice it will be invisible to users for the reasons stated above.
These types are not usable, and were never referenced.
This is preparatory work for #7943.
2021-11-23 18:24:56 +00:00
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sArrayOutput{})\n", name)
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sMapOutput{})\n", name)
|
2021-10-14 17:41:40 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
2021-01-15 18:06:57 +00:00
|
|
|
func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, importsAndAliases map[string]string, seen map[schema.Type]struct{}) {
|
2020-03-19 15:32:40 +00:00
|
|
|
if _, ok := seen[t]; ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
seen[t] = struct{}{}
|
2022-06-22 15:57:05 +00:00
|
|
|
|
|
|
|
// Import an external type with `token` and return true.
|
|
|
|
// If the type is not external, return false.
|
|
|
|
importExternal := func(token string) bool {
|
|
|
|
if pkg.isExternalReference(t) {
|
2022-07-06 18:35:31 +00:00
|
|
|
extPkgCtx, _ := pkg.contextForExternalReference(t)
|
2022-06-22 15:57:05 +00:00
|
|
|
mod := extPkgCtx.tokenToPackage(token)
|
|
|
|
imp := path.Join(extPkgCtx.importBasePath, mod)
|
|
|
|
importsAndAliases[imp] = extPkgCtx.pkgImportAliases[imp]
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
switch t := t.(type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.OptionalType:
|
|
|
|
pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen)
|
|
|
|
case *schema.InputType:
|
|
|
|
pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen)
|
2021-02-26 06:05:02 +00:00
|
|
|
case *schema.EnumType:
|
2022-06-22 15:57:05 +00:00
|
|
|
if importExternal(t.Token) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2021-02-26 06:05:02 +00:00
|
|
|
mod := pkg.tokenToPackage(t.Token)
|
|
|
|
if mod != pkg.mod {
|
|
|
|
p := path.Join(pkg.importBasePath, mod)
|
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, mod)] = pkg.pkgImportAliases[p]
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ArrayType:
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.MapType:
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ObjectType:
|
2022-06-22 15:57:05 +00:00
|
|
|
if importExternal(t.Token) {
|
2021-01-15 18:06:57 +00:00
|
|
|
break
|
|
|
|
}
|
2022-06-22 15:57:05 +00:00
|
|
|
|
2020-07-14 17:58:29 +00:00
|
|
|
mod := pkg.tokenToPackage(t.Token)
|
2020-01-21 22:45:48 +00:00
|
|
|
if mod != pkg.mod {
|
2021-01-15 18:06:57 +00:00
|
|
|
p := path.Join(pkg.importBasePath, mod)
|
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, mod)] = pkg.pkgImportAliases[p]
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 18:06:57 +00:00
|
|
|
if recurse {
|
|
|
|
for _, p := range t.Properties {
|
2022-04-29 16:54:42 +00:00
|
|
|
// We only recurse one level into objects, since we need to name
|
|
|
|
// their properties but not the properties named in their
|
|
|
|
// properties.
|
|
|
|
pkg.getTypeImports(p.Type, false, importsAndAliases, seen)
|
2020-03-17 20:09:43 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2020-11-09 18:55:53 +00:00
|
|
|
case *schema.ResourceType:
|
2022-06-22 15:57:05 +00:00
|
|
|
if importExternal(t.Token) {
|
2021-01-15 18:06:57 +00:00
|
|
|
break
|
|
|
|
}
|
2020-11-09 18:55:53 +00:00
|
|
|
mod := pkg.tokenToPackage(t.Token)
|
|
|
|
if mod != pkg.mod {
|
2021-01-15 18:06:57 +00:00
|
|
|
p := path.Join(pkg.importBasePath, mod)
|
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, mod)] = pkg.pkgImportAliases[p]
|
2020-11-09 18:55:53 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.UnionType:
|
2020-03-17 20:09:43 +00:00
|
|
|
for _, e := range t.ElementTypes {
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(e, recurse, importsAndAliases, seen)
|
2020-03-17 20:09:43 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
func extractImportBasePath(extPkg schema.PackageReference) string {
|
|
|
|
version := extPkg.Version().Major
|
2021-01-15 18:06:57 +00:00
|
|
|
var vPath string
|
|
|
|
if version > 1 {
|
|
|
|
vPath = fmt.Sprintf("/v%d", version)
|
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
return fmt.Sprintf("github.com/pulumi/pulumi-%s/sdk%s/go/%s", extPkg.Name(), vPath, extPkg.Name())
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) getImports(member interface{}, importsAndAliases map[string]string) {
|
2020-03-19 15:32:40 +00:00
|
|
|
seen := map[schema.Type]struct{}{}
|
2020-01-21 22:45:48 +00:00
|
|
|
switch member := member.(type) {
|
|
|
|
case *schema.ObjectType:
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(member, true, importsAndAliases, seen)
|
2020-11-09 18:55:53 +00:00
|
|
|
case *schema.ResourceType:
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(member, true, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.Resource:
|
|
|
|
for _, p := range member.Properties {
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(p.Type, false, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
for _, p := range member.InputProperties {
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(p.Type, false, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
if p.IsRequired() {
|
2022-11-01 09:02:01 +00:00
|
|
|
importsAndAliases["errors"] = ""
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-08 03:11:40 +00:00
|
|
|
for _, method := range member.Methods {
|
|
|
|
if method.Function.Inputs != nil {
|
|
|
|
for _, p := range method.Function.Inputs.InputShape.Properties {
|
|
|
|
if p.Name == "__self__" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pkg.getTypeImports(p.Type, false, importsAndAliases, seen)
|
|
|
|
}
|
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
|
|
|
|
if method.Function.ReturnType != nil {
|
|
|
|
if objectType, ok := method.Function.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
for _, p := range objectType.Properties {
|
|
|
|
pkg.getTypeImports(p.Type, false, importsAndAliases, seen)
|
|
|
|
}
|
|
|
|
} else if method.Function.ReturnTypePlain {
|
|
|
|
pkg.getTypeImports(method.Function.ReturnType, false, importsAndAliases, seen)
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.Function:
|
|
|
|
if member.Inputs != nil {
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(member.Inputs, true, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
|
|
|
|
var returnType *schema.ObjectType
|
|
|
|
if member.ReturnType != nil {
|
|
|
|
if objectType, ok := member.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
|
|
|
returnType = objectType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if returnType != nil {
|
|
|
|
pkg.getTypeImports(returnType, true, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
case []*schema.Property:
|
|
|
|
for _, p := range member {
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getTypeImports(p.Type, false, importsAndAliases, seen)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-15 17:34:42 +00:00
|
|
|
func (pkg *pkgContext) genHeader(w io.Writer, goImports []string, importsAndAliases map[string]string, isUtil bool) {
|
2022-04-19 16:39:23 +00:00
|
|
|
fmt.Fprintf(w, "// Code generated by %v DO NOT EDIT.\n", pkg.tool)
|
|
|
|
fmt.Fprintf(w, "// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***\n\n")
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
var pkgName string
|
|
|
|
if pkg.mod == "" {
|
2023-06-15 17:34:42 +00:00
|
|
|
if isUtil {
|
2023-06-13 18:48:34 +00:00
|
|
|
// we place pulumiVersion and pulumiUtilities in an ./internal folder
|
2023-08-22 17:16:43 +00:00
|
|
|
// the name of the folder can be overridden by the schema
|
|
|
|
// so we use the computed internalModuleName which defaults to "internal" if not set
|
|
|
|
pkgName = pkg.internalModuleName
|
2023-06-13 18:48:34 +00:00
|
|
|
} else {
|
|
|
|
def, err := pkg.pkg.Definition()
|
|
|
|
contract.AssertNoErrorf(err, "Could not retrieve definition")
|
|
|
|
pkgName = packageName(def)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
} else {
|
|
|
|
pkgName = path.Base(pkg.mod)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "package %s\n\n", pkgName)
|
|
|
|
|
|
|
|
var imports []string
|
2021-01-15 18:06:57 +00:00
|
|
|
if len(importsAndAliases) > 0 {
|
|
|
|
for k := range importsAndAliases {
|
2020-01-21 22:45:48 +00:00
|
|
|
imports = append(imports, k)
|
|
|
|
}
|
|
|
|
sort.Strings(imports)
|
2020-03-17 20:09:43 +00:00
|
|
|
|
|
|
|
for i, k := range imports {
|
2021-01-15 18:06:57 +00:00
|
|
|
if alias := importsAndAliases[k]; alias != "" {
|
2020-03-17 20:09:43 +00:00
|
|
|
imports[i] = fmt.Sprintf(`%s "%s"`, alias, k)
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(goImports) > 0 {
|
|
|
|
if len(imports) > 0 {
|
|
|
|
goImports = append(goImports, "")
|
|
|
|
}
|
|
|
|
imports = append(goImports, imports...)
|
|
|
|
}
|
|
|
|
if len(imports) > 0 {
|
|
|
|
fmt.Fprintf(w, "import (\n")
|
|
|
|
for _, i := range imports {
|
|
|
|
if i == "" {
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
} else {
|
2020-03-17 20:09:43 +00:00
|
|
|
if strings.Contains(i, `"`) { // Imports with aliases already include quotes.
|
|
|
|
fmt.Fprintf(w, "\t%s\n", i)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\t%q\n", i)
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, ")\n\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkg *pkgContext) genConfig(w io.Writer, variables []*schema.Property) error {
|
2021-12-10 23:35:24 +00:00
|
|
|
importsAndAliases := map[string]string{
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config": "",
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi": "",
|
|
|
|
}
|
2021-01-15 18:06:57 +00:00
|
|
|
pkg.getImports(variables, importsAndAliases)
|
2023-08-22 17:16:43 +00:00
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, pkg.internalModuleName)] = ""
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(w, nil, importsAndAliases, false /* isUtil */)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-06-13 18:48:34 +00:00
|
|
|
// in case we're not using the internal package, assign to a blank var
|
2023-08-22 17:16:43 +00:00
|
|
|
fmt.Fprintf(w, "var _ = %s.GetEnvOrDefault\n", pkg.internalModuleName)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
|
|
|
for _, p := range variables {
|
|
|
|
getfunc := "Get"
|
|
|
|
|
|
|
|
var getType string
|
|
|
|
var funcType string
|
2021-06-24 16:17:55 +00:00
|
|
|
switch codegen.UnwrapType(p.Type) {
|
2020-01-21 22:45:48 +00:00
|
|
|
case schema.BoolType:
|
|
|
|
getType, funcType = "bool", "Bool"
|
|
|
|
case schema.IntType:
|
|
|
|
getType, funcType = "int", "Int"
|
|
|
|
case schema.NumberType:
|
|
|
|
getType, funcType = "float64", "Float64"
|
|
|
|
default:
|
|
|
|
getType, funcType = "string", ""
|
|
|
|
}
|
|
|
|
|
2020-05-14 00:12:59 +00:00
|
|
|
printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false)
|
2022-12-08 10:45:46 +00:00
|
|
|
configKey := fmt.Sprintf("\"%s:%s\"", pkg.pkg.Name(), cgstrings.Camel(p.Name))
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2020-04-07 00:01:33 +00:00
|
|
|
fmt.Fprintf(w, "func Get%s(ctx *pulumi.Context) %s {\n", Title(p.Name), getType)
|
2020-01-21 22:45:48 +00:00
|
|
|
if p.DefaultValue != nil {
|
|
|
|
fmt.Fprintf(w, "\tv, err := config.Try%s(ctx, %s)\n", funcType, configKey)
|
|
|
|
fmt.Fprintf(w, "\tif err == nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\treturn v\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
fix(sdkgen/go): Don't set default to zero if env is empty
sdkgen has a discrepancy in behavior for default values.
There are three scenarios:
- default value
- default value and an environment variable fallback
- only an environment variable fallback
We handle the first two correctly:
the value is set only if there was something to set it to.
But in the third case, we set variables to their zero value
when the environment variable is empty.
// Current implementation effectively does:
if v := os.Getenv("whatever"); v != "" {
out.Field = pointerOf(v)
} else {
out.Field = pointerOf("")
}
To fix this, we generate code to set these values
only if the environment variable is non-empty.
Implementation notes:
Previosly, getDefaultValue generated an expression
holding the default value.
For fixed default values, it was just the value.
For default values with environment variable fallback,
it took the form:
getEnvOrDefault($default, $parser, $env1, $env2, ...).($type)
For example:
getEnvOrDefault("", nil, "ITEM_NAME").(string)
Given that getDefaultValue returned an expression,
its consumers were able to do something like:
if result == nil {
result = getEnvOrDefault(...).(string)
}
As part of this fix, we need to stop assinging to result
if the environment variable is unset,
so we want getDefaultValue to generate:
if result == nil {
if d := getEnvOrDefault(...); d != nil {
result = d
}
}
To accomplish this, we switch getDefaultValue to setDefaultValue.
Instead of returning an expression, it accepts a callback.
It generates the blocks it needs to, and invokes the callback
with an expression holding the default value if necessary.
Resolves #12971
2023-05-18 18:31:24 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(w, "\tvar value %s\n", getType)
|
|
|
|
err := pkg.setDefaultValue(w, p.DefaultValue, codegen.UnwrapType(p.Type), func(w io.Writer, dv string) error {
|
|
|
|
_, err := fmt.Fprintf(w, "\tvalue = %s\n", dv)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "\treturn value\n")
|
2020-01-21 22:45:48 +00:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "\treturn config.%s%s(ctx, %s)\n", getfunc, funcType, configKey)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-19 23:59:51 +00:00
|
|
|
// genResourceModule generates a ResourceModule definition and the code to register an instance thereof with the
|
|
|
|
// Pulumi runtime. The generated ResourceModule supports the deserialization of resource references into fully-
|
|
|
|
// hydrated Resource instances. If this is the root module, this function also generates a ResourcePackage
|
|
|
|
// definition and its registration to support rehydrating providers.
|
2022-12-08 10:45:46 +00:00
|
|
|
func (pkg *pkgContext) genResourceModule(w io.Writer) error {
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(len(pkg.resources) != 0, "Package must have at least one resource")
|
2021-11-12 00:00:03 +00:00
|
|
|
allResourcesAreOverlays := true
|
|
|
|
for _, r := range pkg.resources {
|
|
|
|
if !r.IsOverlay {
|
|
|
|
allResourcesAreOverlays = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if allResourcesAreOverlays {
|
|
|
|
// If all resources in this module are overlays, skip further code generation.
|
2022-12-08 10:45:46 +00:00
|
|
|
return nil
|
2021-11-12 00:00:03 +00:00
|
|
|
}
|
2021-01-19 23:59:51 +00:00
|
|
|
|
2021-01-15 18:06:57 +00:00
|
|
|
imports := map[string]string{
|
2021-01-15 18:09:09 +00:00
|
|
|
"github.com/blang/semver": "",
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi": "",
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
2023-08-22 17:16:43 +00:00
|
|
|
imports[path.Join(pkg.importBasePath, pkg.internalModuleName)] = ""
|
2021-01-19 23:59:51 +00:00
|
|
|
|
2022-03-19 00:02:33 +00:00
|
|
|
// If there are any internal dependencies, include them as blank imports.
|
2023-06-13 18:48:34 +00:00
|
|
|
def, err := pkg.pkg.Definition()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if goInfo, ok := def.Language["go"].(GoPackageInfo); ok {
|
|
|
|
for _, dep := range goInfo.InternalDependencies {
|
|
|
|
imports[dep] = "_"
|
2022-03-19 00:02:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(w, []string{"fmt"}, imports, false /* isUtil */)
|
2021-01-19 23:59:51 +00:00
|
|
|
|
|
|
|
var provider *schema.Resource
|
|
|
|
registrations := codegen.StringSet{}
|
|
|
|
if providerOnly := len(pkg.resources) == 1 && pkg.resources[0].IsProvider; providerOnly {
|
|
|
|
provider = pkg.resources[0]
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "type module struct {\n")
|
|
|
|
fmt.Fprintf(w, "\tversion semver.Version\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (m *module) Version() semver.Version {\n")
|
|
|
|
fmt.Fprintf(w, "\treturn m.version\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi.Resource, err error) {\n")
|
|
|
|
fmt.Fprintf(w, "\tswitch typ {\n")
|
|
|
|
for _, r := range pkg.resources {
|
2021-11-12 00:00:03 +00:00
|
|
|
if r.IsOverlay {
|
|
|
|
// This resource code is generated by the provider, so no further action is required.
|
|
|
|
continue
|
|
|
|
}
|
2021-01-19 23:59:51 +00:00
|
|
|
if r.IsProvider {
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.Assertf(provider == nil, "Provider must not be specified for Provider resources")
|
2021-01-19 23:59:51 +00:00
|
|
|
provider = r
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
registrations.Add(tokenToModule(r.Token))
|
|
|
|
fmt.Fprintf(w, "\tcase %q:\n", r.Token)
|
2021-09-04 02:42:45 +00:00
|
|
|
fmt.Fprintf(w, "\t\tr = &%s{}\n", disambiguatedResourceName(r, pkg))
|
2021-01-19 23:59:51 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\tdefault:\n")
|
|
|
|
fmt.Fprintf(w, "\t\treturn nil, fmt.Errorf(\"unknown resource type: %%s\", typ)\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n\n")
|
2021-03-31 22:50:30 +00:00
|
|
|
fmt.Fprintf(w, "\terr = ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn))\n")
|
2021-01-19 23:59:51 +00:00
|
|
|
fmt.Fprintf(w, "\treturn\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if provider != nil {
|
|
|
|
fmt.Fprintf(w, "type pkg struct {\n")
|
|
|
|
fmt.Fprintf(w, "\tversion semver.Version\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (p *pkg) Version() semver.Version {\n")
|
|
|
|
fmt.Fprintf(w, "\treturn p.version\n")
|
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func (p *pkg) ConstructProvider(ctx *pulumi.Context, name, typ, urn string) (pulumi.ProviderResource, error) {\n")
|
2022-12-08 10:45:46 +00:00
|
|
|
fmt.Fprintf(w, "\tif typ != \"pulumi:providers:%s\" {\n", pkg.pkg.Name())
|
2021-01-19 23:59:51 +00:00
|
|
|
fmt.Fprintf(w, "\t\treturn nil, fmt.Errorf(\"unknown provider type: %%s\", typ)\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n\n")
|
2021-03-31 22:50:30 +00:00
|
|
|
fmt.Fprintf(w, "\tr := &Provider{}\n")
|
|
|
|
fmt.Fprintf(w, "\terr := ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn))\n")
|
|
|
|
fmt.Fprintf(w, "\treturn r, err\n")
|
2021-01-19 23:59:51 +00:00
|
|
|
fmt.Fprintf(w, "}\n\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "func init() {\n")
|
2023-06-12 23:23:54 +00:00
|
|
|
|
2023-08-22 17:16:43 +00:00
|
|
|
fmt.Fprintf(w, "\tversion, err := %s.PkgVersion()\n", pkg.internalModuleName)
|
2023-06-12 23:23:54 +00:00
|
|
|
// To avoid breaking compatibility, we don't change the function
|
|
|
|
// signature. We instead just ignore the error.
|
|
|
|
fmt.Fprintf(w, "\tif err != nil {\n")
|
|
|
|
fmt.Fprintf(w, "\t\tversion = semver.Version{Major: 1}\n")
|
|
|
|
fmt.Fprintf(w, "\t}\n")
|
2021-01-19 23:59:51 +00:00
|
|
|
if len(registrations) > 0 {
|
|
|
|
for _, mod := range registrations.SortedValues() {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterResourceModule(\n")
|
2022-12-08 10:45:46 +00:00
|
|
|
fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name())
|
2021-01-19 23:59:51 +00:00
|
|
|
fmt.Fprintf(w, "\t\t%q,\n", mod)
|
|
|
|
fmt.Fprintf(w, "\t\t&module{version},\n")
|
|
|
|
fmt.Fprintf(w, "\t)\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if provider != nil {
|
|
|
|
fmt.Fprintf(w, "\tpulumi.RegisterResourcePackage(\n")
|
2022-12-08 10:45:46 +00:00
|
|
|
fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name())
|
2021-01-19 23:59:51 +00:00
|
|
|
fmt.Fprintf(w, "\t\t&pkg{version},\n")
|
|
|
|
fmt.Fprintf(w, "\t)\n")
|
|
|
|
}
|
2023-06-13 18:48:34 +00:00
|
|
|
_, err = fmt.Fprintf(w, "}\n")
|
2022-12-08 10:45:46 +00:00
|
|
|
return err
|
2021-01-19 23:59:51 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 15:17:58 +00:00
|
|
|
// generatePackageContextMap groups resources, types, and functions into Go packages.
|
2022-12-08 10:45:46 +00:00
|
|
|
func generatePackageContextMap(tool string, pkg schema.PackageReference, goInfo GoPackageInfo, externalPkgs *Cache) (map[string]*pkgContext, error) {
|
2020-01-21 22:45:48 +00:00
|
|
|
packages := map[string]*pkgContext{}
|
2022-08-12 18:04:21 +00:00
|
|
|
|
|
|
|
// Share the cache
|
|
|
|
if externalPkgs == nil {
|
|
|
|
externalPkgs = globalCache
|
|
|
|
}
|
|
|
|
|
2020-06-03 01:15:21 +00:00
|
|
|
getPkg := func(mod string) *pkgContext {
|
2020-01-21 22:45:48 +00:00
|
|
|
pack, ok := packages[mod]
|
|
|
|
if !ok {
|
2023-08-22 17:16:43 +00:00
|
|
|
internalModuleName := "internal"
|
|
|
|
if goInfo.InternalModuleName != "" {
|
|
|
|
internalModuleName = goInfo.InternalModuleName
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
pack = &pkgContext{
|
2021-10-12 17:15:24 +00:00
|
|
|
pkg: pkg,
|
|
|
|
mod: mod,
|
|
|
|
importBasePath: goInfo.ImportBasePath,
|
|
|
|
rootPackageName: goInfo.RootPackageName,
|
|
|
|
typeDetails: map[schema.Type]*typeDetails{},
|
|
|
|
names: codegen.NewStringSet(),
|
|
|
|
schemaNames: codegen.NewStringSet(),
|
|
|
|
renamed: map[string]string{},
|
|
|
|
duplicateTokens: map[string]bool{},
|
|
|
|
functionNames: map[*schema.Function]string{},
|
|
|
|
tool: tool,
|
|
|
|
modToPkg: goInfo.ModuleToPackage,
|
|
|
|
pkgImportAliases: goInfo.PackageImportAliases,
|
|
|
|
packages: packages,
|
|
|
|
liftSingleValueMethodReturns: goInfo.LiftSingleValueMethodReturns,
|
|
|
|
disableInputTypeRegistrations: goInfo.DisableInputTypeRegistrations,
|
2021-11-23 23:10:15 +00:00
|
|
|
disableObjectDefaults: goInfo.DisableObjectDefaults,
|
2023-08-22 17:16:43 +00:00
|
|
|
internalModuleName: internalModuleName,
|
2022-08-12 18:04:21 +00:00
|
|
|
externalPackages: externalPkgs,
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
packages[mod] = pack
|
|
|
|
}
|
|
|
|
return pack
|
|
|
|
}
|
|
|
|
|
2020-06-03 01:15:21 +00:00
|
|
|
getPkgFromToken := func(token string) *pkgContext {
|
2020-07-14 17:58:29 +00:00
|
|
|
return getPkg(tokenToPackage(pkg, goInfo.ModuleToPackage, token))
|
2020-06-03 01:15:21 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
var getPkgFromType func(schema.Type) *pkgContext
|
|
|
|
getPkgFromType = func(typ schema.Type) *pkgContext {
|
2021-06-24 16:17:55 +00:00
|
|
|
switch t := codegen.UnwrapType(typ).(type) {
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.ArrayType:
|
|
|
|
return getPkgFromType(t.ElementType)
|
|
|
|
case *schema.MapType:
|
|
|
|
return getPkgFromType(t.ElementType)
|
2023-12-05 12:29:30 +00:00
|
|
|
case *schema.ObjectType:
|
|
|
|
return getPkgFromToken(t.Token)
|
|
|
|
case *schema.EnumType:
|
|
|
|
return getPkgFromToken(t.Token)
|
2021-03-11 05:04:30 +00:00
|
|
|
default:
|
|
|
|
return getPkgFromToken(t.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
config, err := pkg.Config()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(config) > 0 {
|
2020-06-03 01:15:21 +00:00
|
|
|
_ = getPkg("config")
|
2020-04-14 08:24:32 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 20:33:38 +00:00
|
|
|
// For any optional properties, we must generate a pointer type for the corresponding property type.
|
|
|
|
// In addition, if the optional property's type is itself an object type, we also need to generate pointer
|
|
|
|
// types corresponding to all of it's nested properties, as our accessor methods will lift `nil` into
|
|
|
|
// those nested types.
|
2021-12-10 23:35:24 +00:00
|
|
|
var populateDetailsForPropertyTypes func(seen codegen.StringSet, props []*schema.Property, optional, input, output bool)
|
|
|
|
var populateDetailsForTypes func(seen codegen.StringSet, schemaType schema.Type, optional, input, output bool)
|
2021-03-11 05:04:30 +00:00
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
seenKey := func(t schema.Type, optional, input, output bool) string {
|
|
|
|
var key string
|
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.ObjectType:
|
|
|
|
key = t.Token
|
|
|
|
case *schema.EnumType:
|
|
|
|
key = t.Token
|
|
|
|
default:
|
|
|
|
key = t.String()
|
|
|
|
}
|
|
|
|
if optional {
|
|
|
|
key += ",optional"
|
|
|
|
}
|
|
|
|
if input {
|
|
|
|
key += ",input"
|
|
|
|
}
|
|
|
|
if output {
|
|
|
|
key += ",output"
|
|
|
|
}
|
|
|
|
return key
|
|
|
|
}
|
|
|
|
|
|
|
|
populateDetailsForPropertyTypes = func(seen codegen.StringSet, props []*schema.Property, optional, input, output bool) {
|
2020-04-21 20:33:38 +00:00
|
|
|
for _, p := range props {
|
2022-05-03 18:13:21 +00:00
|
|
|
if obj, ok := codegen.UnwrapType(p.Type).(*schema.ObjectType); ok && p.Plain {
|
|
|
|
pkg := getPkgFromToken(obj.Token)
|
|
|
|
details := pkg.detailsForType(obj)
|
|
|
|
details.mark(true, false)
|
|
|
|
input = true
|
|
|
|
_, hasOptional := p.Type.(*schema.OptionalType)
|
|
|
|
details.markPtr(hasOptional, false)
|
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
populateDetailsForTypes(seen, p.Type, !p.IsRequired() || optional, input, output)
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-25 18:08:05 +00:00
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
populateDetailsForTypes = func(seen codegen.StringSet, schemaType schema.Type, optional, input, output bool) {
|
|
|
|
key := seenKey(schemaType, optional, input, output)
|
|
|
|
if seen.Has(key) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
seen.Add(key)
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
switch typ := schemaType.(type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.InputType:
|
2021-12-10 23:35:24 +00:00
|
|
|
populateDetailsForTypes(seen, typ.ElementType, optional, true, false)
|
2021-06-24 16:17:55 +00:00
|
|
|
case *schema.OptionalType:
|
2021-12-10 23:35:24 +00:00
|
|
|
populateDetailsForTypes(seen, typ.ElementType, true, input, output)
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.ObjectType:
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg := getPkgFromToken(typ.Token)
|
2021-12-10 23:35:24 +00:00
|
|
|
pkg.detailsForType(typ).mark(input || goInfo.GenerateExtraInputTypes, output)
|
|
|
|
|
|
|
|
if optional {
|
|
|
|
pkg.detailsForType(typ).markPtr(input || goInfo.GenerateExtraInputTypes, output)
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg.schemaNames.Add(tokenToName(typ.Token))
|
2021-12-10 23:35:24 +00:00
|
|
|
|
|
|
|
populateDetailsForPropertyTypes(seen, typ.Properties, optional, input, output)
|
2023-11-29 12:37:53 +00:00
|
|
|
case *schema.UnionType:
|
|
|
|
for _, e := range typ.ElementTypes {
|
|
|
|
populateDetailsForTypes(seen, e, optional, input, output)
|
|
|
|
}
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.EnumType:
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg := getPkgFromToken(typ.Token)
|
2021-12-10 23:35:24 +00:00
|
|
|
pkg.detailsForType(typ).mark(input || goInfo.GenerateExtraInputTypes, output)
|
|
|
|
|
|
|
|
if optional {
|
|
|
|
pkg.detailsForType(typ).markPtr(input || goInfo.GenerateExtraInputTypes, output)
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg.schemaNames.Add(tokenToName(typ.Token))
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.ArrayType:
|
2021-12-10 23:35:24 +00:00
|
|
|
details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType))
|
|
|
|
details.markArray(input || goInfo.GenerateExtraInputTypes, output)
|
|
|
|
populateDetailsForTypes(seen, typ.ElementType, false, input, output)
|
2021-03-11 05:04:30 +00:00
|
|
|
case *schema.MapType:
|
2021-12-10 23:35:24 +00:00
|
|
|
details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType))
|
|
|
|
details.markMap(input || goInfo.GenerateExtraInputTypes, output)
|
|
|
|
populateDetailsForTypes(seen, typ.ElementType, false, input, output)
|
2020-04-21 20:33:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 14:33:14 +00:00
|
|
|
// Rewrite cyclic types. See the docs on rewriteCyclicFields for the motivation.
|
2022-12-08 10:45:46 +00:00
|
|
|
def, err := pkg.Definition()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rewriteCyclicObjectFields(def)
|
2021-09-28 14:33:14 +00:00
|
|
|
|
2020-04-22 23:49:57 +00:00
|
|
|
// Use a string set to track object types that have already been processed.
|
|
|
|
// This avoids recursively processing the same type. For example, in the
|
|
|
|
// Kubernetes package, JSONSchemaProps have properties whose type is itself.
|
2021-01-04 21:22:56 +00:00
|
|
|
seenMap := codegen.NewStringSet()
|
2022-12-08 10:45:46 +00:00
|
|
|
for _, t := range def.Types {
|
2020-11-03 07:02:56 +00:00
|
|
|
switch typ := t.(type) {
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ArrayType:
|
2021-12-10 23:35:24 +00:00
|
|
|
details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType))
|
|
|
|
details.markArray(goInfo.GenerateExtraInputTypes, false)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.MapType:
|
2021-12-10 23:35:24 +00:00
|
|
|
details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType))
|
|
|
|
details.markMap(goInfo.GenerateExtraInputTypes, false)
|
2020-01-21 22:45:48 +00:00
|
|
|
case *schema.ObjectType:
|
2020-11-03 07:02:56 +00:00
|
|
|
pkg := getPkgFromToken(typ.Token)
|
2021-06-24 16:17:55 +00:00
|
|
|
if !typ.IsInputShape() {
|
|
|
|
pkg.types = append(pkg.types, typ)
|
|
|
|
}
|
2021-12-10 23:35:24 +00:00
|
|
|
populateDetailsForTypes(seenMap, typ, false, false, false)
|
2020-11-03 07:02:56 +00:00
|
|
|
case *schema.EnumType:
|
2021-11-16 23:53:28 +00:00
|
|
|
if !typ.IsOverlay {
|
|
|
|
pkg := getPkgFromToken(typ.Token)
|
|
|
|
pkg.enums = append(pkg.enums, typ)
|
2021-12-10 23:35:24 +00:00
|
|
|
|
|
|
|
populateDetailsForTypes(seenMap, typ, false, false, false)
|
2021-11-16 23:53:28 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-04 02:42:45 +00:00
|
|
|
resSeen := map[string]bool{}
|
|
|
|
typeSeen := map[string]bool{}
|
|
|
|
|
|
|
|
// compute set of names generated by a resource
|
|
|
|
// handling any potential collisions via remapping along the way
|
2020-01-21 22:45:48 +00:00
|
|
|
scanResource := func(r *schema.Resource) {
|
2021-09-22 03:48:45 +00:00
|
|
|
if resSeen[strings.ToLower(r.Token)] {
|
2021-09-04 02:42:45 +00:00
|
|
|
return
|
|
|
|
}
|
2021-09-22 03:48:45 +00:00
|
|
|
resSeen[strings.ToLower(r.Token)] = true
|
2020-06-03 01:15:21 +00:00
|
|
|
pkg := getPkgFromToken(r.Token)
|
2020-01-21 22:45:48 +00:00
|
|
|
pkg.resources = append(pkg.resources, r)
|
2021-03-31 05:23:04 +00:00
|
|
|
pkg.schemaNames.Add(tokenToName(r.Token))
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2021-09-04 02:42:45 +00:00
|
|
|
getNames := func(suffix string) []string {
|
|
|
|
names := []string{}
|
|
|
|
names = append(names, rawResourceName(r)+suffix)
|
|
|
|
names = append(names, rawResourceName(r)+suffix+"Input")
|
|
|
|
names = append(names, rawResourceName(r)+suffix+"Output")
|
|
|
|
names = append(names, rawResourceName(r)+suffix+"Args")
|
2022-10-17 16:37:07 +00:00
|
|
|
names = append(names, cgstrings.Camel(rawResourceName(r))+suffix+"Args")
|
2021-09-04 02:42:45 +00:00
|
|
|
names = append(names, "New"+rawResourceName(r)+suffix)
|
|
|
|
if !r.IsProvider && !r.IsComponent {
|
|
|
|
names = append(names, rawResourceName(r)+suffix+"State")
|
2022-10-17 16:37:07 +00:00
|
|
|
names = append(names, cgstrings.Camel(rawResourceName(r))+suffix+"State")
|
2021-09-04 02:42:45 +00:00
|
|
|
names = append(names, "Get"+rawResourceName(r)+suffix)
|
|
|
|
}
|
2022-11-03 00:16:08 +00:00
|
|
|
if goInfo.GenerateResourceContainerTypes && !r.IsProvider {
|
|
|
|
names = append(names, rawResourceName(r)+suffix+"Array")
|
|
|
|
names = append(names, rawResourceName(r)+suffix+"Map")
|
|
|
|
}
|
2021-09-04 02:42:45 +00:00
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
|
|
|
suffixes := []string{"", "Resource", "Res"}
|
|
|
|
suffix := ""
|
|
|
|
suffixIndex := 0
|
|
|
|
canGenerate := false
|
|
|
|
|
|
|
|
for !canGenerate && suffixIndex <= len(suffixes) {
|
|
|
|
suffix = suffixes[suffixIndex]
|
|
|
|
candidates := getNames(suffix)
|
|
|
|
conflict := false
|
|
|
|
for _, c := range candidates {
|
|
|
|
if pkg.names.Has(c) {
|
|
|
|
conflict = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !conflict {
|
|
|
|
canGenerate = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
suffixIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
if !canGenerate {
|
2023-12-12 12:19:42 +00:00
|
|
|
panic("unable to generate Go SDK, schema has unresolvable overlapping resource: " + rawResourceName(r))
|
2021-09-04 02:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
names := getNames(suffix)
|
|
|
|
originalNames := getNames("")
|
|
|
|
for i, n := range names {
|
|
|
|
pkg.names.Add(n)
|
|
|
|
if suffix != "" {
|
|
|
|
pkg.renamed[originalNames[i]] = names[i]
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-12-10 23:35:24 +00:00
|
|
|
populateDetailsForPropertyTypes(seenMap, r.InputProperties, r.IsProvider, false, false)
|
|
|
|
populateDetailsForPropertyTypes(seenMap, r.Properties, r.IsProvider, false, true)
|
2021-07-08 03:11:40 +00:00
|
|
|
|
2022-01-10 20:03:20 +00:00
|
|
|
if r.StateInputs != nil {
|
|
|
|
populateDetailsForPropertyTypes(seenMap, r.StateInputs.Properties,
|
|
|
|
r.IsProvider, false /*input*/, false /*output*/)
|
|
|
|
}
|
|
|
|
|
2021-07-08 03:11:40 +00:00
|
|
|
for _, method := range r.Methods {
|
|
|
|
if method.Function.Inputs != nil {
|
2021-09-04 02:42:45 +00:00
|
|
|
pkg.names.Add(rawResourceName(r) + Title(method.Name) + "Args")
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
if method.Function.ReturnType != nil {
|
|
|
|
if _, ok := method.Function.ReturnType.(*schema.ObjectType); ok {
|
|
|
|
pkg.names.Add(rawResourceName(r) + Title(method.Name) + "Result")
|
|
|
|
}
|
2021-07-08 03:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
scanResource(def.Provider)
|
|
|
|
for _, r := range def.Resources {
|
2020-01-21 22:45:48 +00:00
|
|
|
scanResource(r)
|
|
|
|
}
|
|
|
|
|
2021-09-04 02:42:45 +00:00
|
|
|
// compute set of names generated by a type
|
|
|
|
// handling any potential collisions via remapping along the way
|
|
|
|
scanType := func(t schema.Type) {
|
2021-09-23 22:42:48 +00:00
|
|
|
getNames := func(name, suffix string) []string {
|
|
|
|
return []string{name + suffix, name + suffix + "Input", name + suffix + "Output"}
|
|
|
|
}
|
|
|
|
|
2021-09-04 02:42:45 +00:00
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.ObjectType:
|
|
|
|
pkg := getPkgFromToken(t.Token)
|
|
|
|
// maintain support for duplicate tokens for types and resources in Kubernetes
|
2021-09-22 03:48:45 +00:00
|
|
|
if resSeen[strings.ToLower(t.Token)] {
|
|
|
|
pkg.duplicateTokens[strings.ToLower(t.Token)] = true
|
2021-09-04 02:42:45 +00:00
|
|
|
}
|
2021-09-22 03:48:45 +00:00
|
|
|
if typeSeen[strings.ToLower(t.Token)] {
|
2021-09-04 02:42:45 +00:00
|
|
|
return
|
|
|
|
}
|
2021-09-22 03:48:45 +00:00
|
|
|
typeSeen[strings.ToLower(t.Token)] = true
|
2021-09-04 02:42:45 +00:00
|
|
|
|
|
|
|
name := pkg.tokenToType(t.Token)
|
|
|
|
suffixes := []string{"", "Type", "Typ"}
|
|
|
|
suffix := ""
|
|
|
|
suffixIndex := 0
|
|
|
|
canGenerate := false
|
|
|
|
|
|
|
|
for !canGenerate && suffixIndex <= len(suffixes) {
|
|
|
|
suffix = suffixes[suffixIndex]
|
2021-09-23 22:42:48 +00:00
|
|
|
candidates := getNames(name, suffix)
|
|
|
|
conflict := false
|
|
|
|
for _, c := range candidates {
|
|
|
|
if pkg.names.Has(c) {
|
|
|
|
conflict = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !conflict {
|
|
|
|
canGenerate = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
suffixIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
if !canGenerate {
|
2023-12-12 12:19:42 +00:00
|
|
|
panic("unable to generate Go SDK, schema has unresolvable overlapping type: " + name)
|
2021-09-23 22:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
names := getNames(name, suffix)
|
|
|
|
originalNames := getNames(name, "")
|
|
|
|
for i, n := range names {
|
|
|
|
pkg.names.Add(n)
|
|
|
|
if suffix != "" {
|
|
|
|
pkg.renamed[originalNames[i]] = names[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *schema.EnumType:
|
|
|
|
pkg := getPkgFromToken(t.Token)
|
|
|
|
if resSeen[t.Token] {
|
|
|
|
pkg.duplicateTokens[strings.ToLower(t.Token)] = true
|
|
|
|
}
|
|
|
|
if typeSeen[t.Token] {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
typeSeen[t.Token] = true
|
|
|
|
|
|
|
|
name := pkg.tokenToEnum(t.Token)
|
|
|
|
suffixes := []string{"", "Enum"}
|
|
|
|
suffix := ""
|
|
|
|
suffixIndex := 0
|
|
|
|
canGenerate := false
|
|
|
|
|
|
|
|
for !canGenerate && suffixIndex <= len(suffixes) {
|
|
|
|
suffix = suffixes[suffixIndex]
|
|
|
|
candidates := getNames(name, suffix)
|
2021-09-04 02:42:45 +00:00
|
|
|
conflict := false
|
|
|
|
for _, c := range candidates {
|
|
|
|
if pkg.names.Has(c) {
|
|
|
|
conflict = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !conflict {
|
|
|
|
canGenerate = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
suffixIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
if !canGenerate {
|
2023-12-12 12:19:42 +00:00
|
|
|
panic("unable to generate Go SDK, schema has unresolvable overlapping type: " + name)
|
2021-09-04 02:42:45 +00:00
|
|
|
}
|
|
|
|
|
2021-09-23 22:42:48 +00:00
|
|
|
names := getNames(name, suffix)
|
|
|
|
originalNames := getNames(name, "")
|
2021-09-04 02:42:45 +00:00
|
|
|
for i, n := range names {
|
|
|
|
pkg.names.Add(n)
|
|
|
|
if suffix != "" {
|
|
|
|
pkg.renamed[originalNames[i]] = names[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
for _, t := range def.Types {
|
2021-09-04 02:42:45 +00:00
|
|
|
scanType(t)
|
|
|
|
}
|
|
|
|
|
2021-08-23 20:46:09 +00:00
|
|
|
// For fnApply function versions, we need to register any
|
|
|
|
// input or output property type metadata, in case they have
|
|
|
|
// types used in array or pointer element positions.
|
2021-12-10 23:35:24 +00:00
|
|
|
if !goInfo.DisableFunctionOutputVersions || goInfo.GenerateExtraInputTypes {
|
2022-12-08 10:45:46 +00:00
|
|
|
for _, f := range def.Functions {
|
2021-12-10 23:35:24 +00:00
|
|
|
if f.NeedsOutputVersion() || goInfo.GenerateExtraInputTypes {
|
|
|
|
optional := false
|
|
|
|
if f.Inputs != nil {
|
|
|
|
populateDetailsForPropertyTypes(seenMap, f.Inputs.InputShape.Properties, optional, false, false)
|
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
if f.ReturnType != nil {
|
|
|
|
populateDetailsForTypes(seenMap, f.ReturnType, optional, false, true)
|
2021-12-10 23:35:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
for _, f := range def.Functions {
|
2021-07-08 03:11:40 +00:00
|
|
|
if f.IsMethod {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-06-03 01:15:21 +00:00
|
|
|
pkg := getPkgFromToken(f.Token)
|
2020-01-21 22:45:48 +00:00
|
|
|
pkg.functions = append(pkg.functions, f)
|
|
|
|
|
|
|
|
name := tokenToName(f.Token)
|
2021-11-25 01:13:47 +00:00
|
|
|
|
|
|
|
if pkg.names.Has(name) ||
|
|
|
|
pkg.names.Has(name+"Args") ||
|
|
|
|
pkg.names.Has(name+"Result") {
|
2020-01-21 22:45:48 +00:00
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(name, "New"):
|
|
|
|
name = "Create" + name[3:]
|
|
|
|
case strings.HasPrefix(name, "Get"):
|
|
|
|
name = "Lookup" + name[3:]
|
|
|
|
}
|
|
|
|
}
|
2021-01-04 21:22:56 +00:00
|
|
|
pkg.names.Add(name)
|
2020-01-21 22:45:48 +00:00
|
|
|
pkg.functionNames[f] = name
|
|
|
|
|
2023-01-11 22:17:14 +00:00
|
|
|
if f.Inputs != nil && !f.MultiArgumentInputs {
|
2021-01-04 21:22:56 +00:00
|
|
|
pkg.names.Add(name + "Args")
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2023-01-11 22:17:14 +00:00
|
|
|
|
|
|
|
if f.ReturnType != nil {
|
|
|
|
if objectType, ok := f.ReturnType.(*schema.ObjectType); ok && objectType != nil {
|
|
|
|
pkg.names.Add(name + "Result")
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
return packages, nil
|
2020-03-20 15:17:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-01 17:52:07 +00:00
|
|
|
// LanguageResource is derived from the schema and can be used by downstream codegen.
|
|
|
|
type LanguageResource struct {
|
|
|
|
*schema.Resource
|
|
|
|
|
|
|
|
Alias string // The package alias (e.g. appsv1)
|
|
|
|
Name string // The resource name (e.g. Deployment)
|
|
|
|
Package string // The package name (e.g. github.com/pulumi/pulumi-kubernetes/sdk/v2/go/kubernetes/apps/v1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LanguageResources returns a map of resources that can be used by downstream codegen. The map
|
|
|
|
// key is the resource schema token.
|
|
|
|
func LanguageResources(tool string, pkg *schema.Package) (map[string]LanguageResource, error) {
|
|
|
|
resources := map[string]LanguageResource{}
|
|
|
|
|
|
|
|
if err := pkg.ImportLanguages(map[string]schema.Language{"go": Importer}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-19 20:58:13 +00:00
|
|
|
var goPkgInfo GoPackageInfo
|
|
|
|
if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok {
|
|
|
|
goPkgInfo = goInfo
|
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
packages, err := generatePackageContextMap(tool, pkg.Reference(), goPkgInfo, globalCache)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-01 17:52:07 +00:00
|
|
|
|
|
|
|
// emit each package
|
2023-06-28 16:02:04 +00:00
|
|
|
pkgMods := slice.Prealloc[string](len(packages))
|
2020-05-01 17:52:07 +00:00
|
|
|
for mod := range packages {
|
|
|
|
pkgMods = append(pkgMods, mod)
|
|
|
|
}
|
|
|
|
sort.Strings(pkgMods)
|
|
|
|
|
|
|
|
for _, mod := range pkgMods {
|
|
|
|
if mod == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pkg := packages[mod]
|
|
|
|
|
|
|
|
for _, r := range pkg.resources {
|
2021-11-12 00:00:03 +00:00
|
|
|
if r.IsOverlay {
|
|
|
|
// This resource code is generated by the provider, so no further action is required.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-08-19 20:58:13 +00:00
|
|
|
packagePath := path.Join(goPkgInfo.ImportBasePath, pkg.mod)
|
2020-05-01 17:52:07 +00:00
|
|
|
resources[r.Token] = LanguageResource{
|
|
|
|
Resource: r,
|
2020-08-19 20:58:13 +00:00
|
|
|
Alias: goPkgInfo.PackageImportAliases[packagePath],
|
2020-05-01 17:52:07 +00:00
|
|
|
Name: tokenToName(r.Token),
|
|
|
|
Package: packagePath,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resources, nil
|
|
|
|
}
|
|
|
|
|
2021-10-11 22:28:11 +00:00
|
|
|
// packageRoot is the relative root file for go code. That means that every go
|
|
|
|
// source file should be under this root. For example:
|
|
|
|
//
|
|
|
|
// root = aws => sdk/go/aws/*.go
|
2022-12-08 10:45:46 +00:00
|
|
|
func packageRoot(pkg schema.PackageReference) (string, error) {
|
|
|
|
def, err := pkg.Definition()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2021-10-11 22:28:11 +00:00
|
|
|
var info GoPackageInfo
|
2022-12-08 10:45:46 +00:00
|
|
|
if goInfo, ok := def.Language["go"].(GoPackageInfo); ok {
|
2021-10-11 22:28:11 +00:00
|
|
|
info = goInfo
|
|
|
|
}
|
|
|
|
if info.RootPackageName != "" {
|
|
|
|
// package structure is flat
|
2022-12-08 10:45:46 +00:00
|
|
|
return "", nil
|
2021-10-11 22:28:11 +00:00
|
|
|
}
|
|
|
|
if info.ImportBasePath != "" {
|
2022-12-08 10:45:46 +00:00
|
|
|
return path.Base(info.ImportBasePath), nil
|
2021-10-11 22:28:11 +00:00
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
return goPackage(pkg.Name()), nil
|
2021-10-11 22:28:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// packageName is the go package name for the generated package.
|
|
|
|
func packageName(pkg *schema.Package) string {
|
|
|
|
var info GoPackageInfo
|
|
|
|
if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok {
|
|
|
|
info = goInfo
|
|
|
|
}
|
|
|
|
if info.RootPackageName != "" {
|
|
|
|
return info.RootPackageName
|
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
root, err := packageRoot(pkg.Reference())
|
|
|
|
contract.AssertNoErrorf(err, "We generated the ref from a pkg, so we know its a valid ref")
|
|
|
|
return goPackage(root)
|
2021-10-11 22:28:11 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:28:50 +00:00
|
|
|
func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error) {
|
2020-04-20 23:36:05 +00:00
|
|
|
if err := pkg.ImportLanguages(map[string]schema.Language{"go": Importer}); err != nil {
|
|
|
|
return nil, err
|
2020-03-20 15:17:58 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 20:58:13 +00:00
|
|
|
var goPkgInfo GoPackageInfo
|
|
|
|
if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok {
|
|
|
|
goPkgInfo = goInfo
|
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
packages, err := generatePackageContextMap(tool, pkg.Reference(), goPkgInfo, NewCache())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-20 15:17:58 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
// emit each package
|
2023-06-28 16:02:04 +00:00
|
|
|
pkgMods := slice.Prealloc[string](len(packages))
|
2020-01-21 22:45:48 +00:00
|
|
|
for mod := range packages {
|
|
|
|
pkgMods = append(pkgMods, mod)
|
|
|
|
}
|
|
|
|
sort.Strings(pkgMods)
|
|
|
|
|
2021-10-11 22:28:11 +00:00
|
|
|
name := packageName(pkg)
|
2022-12-08 10:45:46 +00:00
|
|
|
pathPrefix, err := packageRoot(pkg.Reference())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-11 22:28:11 +00:00
|
|
|
|
2022-11-09 19:49:59 +00:00
|
|
|
files := codegen.Fs{}
|
2021-12-06 21:10:30 +00:00
|
|
|
|
2021-12-15 18:41:44 +00:00
|
|
|
// Generate pulumi-plugin.json
|
2022-02-03 16:07:13 +00:00
|
|
|
pulumiPlugin := &plugin.PulumiPluginJSON{
|
2021-12-06 21:10:30 +00:00
|
|
|
Resource: true,
|
|
|
|
Name: pkg.Name,
|
|
|
|
Server: pkg.PluginDownloadURL,
|
2022-02-03 16:07:13 +00:00
|
|
|
}
|
|
|
|
if goPkgInfo.RespectSchemaVersion && pkg.Version != nil {
|
|
|
|
pulumiPlugin.Version = pkg.Version.String()
|
|
|
|
}
|
|
|
|
pulumiPluginJSON, err := pulumiPlugin.JSON()
|
2021-12-06 21:10:30 +00:00
|
|
|
if err != nil {
|
2021-12-15 18:41:44 +00:00
|
|
|
return nil, fmt.Errorf("Failed to format pulumi-plugin.json: %w", err)
|
2021-12-06 21:10:30 +00:00
|
|
|
}
|
2022-11-09 19:49:59 +00:00
|
|
|
files.Add(path.Join(pathPrefix, "pulumi-plugin.json"), pulumiPluginJSON)
|
2021-12-06 21:10:30 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
setFileContent := func(root, relPath, contents string) {
|
|
|
|
relPath = path.Join(root, relPath)
|
2020-03-19 15:32:40 +00:00
|
|
|
|
|
|
|
// Run Go formatter on the code before saving to disk
|
|
|
|
formattedSource, err := format.Source([]byte(contents))
|
|
|
|
if err != nil {
|
2021-01-04 21:22:56 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "Invalid content:\n%s\n%s\n", relPath, contents)
|
2021-11-13 02:37:17 +00:00
|
|
|
panic(fmt.Errorf("invalid Go source code:\n\n%s\n: %w", relPath, err))
|
2020-03-19 15:32:40 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 19:49:59 +00:00
|
|
|
files.Add(relPath, formattedSource)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if goPkgInfo.Generics == "" {
|
|
|
|
// default is emitting the non-generic variant only
|
|
|
|
goPkgInfo.Generics = GenericsSettingNone
|
|
|
|
}
|
|
|
|
|
|
|
|
emitOnlyGenericVariant := goPkgInfo.Generics == GenericsSettingGenericsOnly
|
|
|
|
emitOnlyLegacyVariant := goPkgInfo.Generics == GenericsSettingNone
|
|
|
|
|
|
|
|
setFile := func(relPath, contents string) {
|
|
|
|
if emitOnlyGenericVariant {
|
|
|
|
// if we only want the generic variant to be emitted
|
|
|
|
// skip generating the default "legacy" variant
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
setFileContent(pathPrefix, relPath, contents)
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenericVariantFile := func(relPath, contents string) {
|
|
|
|
if emitOnlyLegacyVariant {
|
|
|
|
// if we only want the legacy variant to be emitted
|
|
|
|
// skip generating the generic variant
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
root := path.Join(pathPrefix, "x")
|
|
|
|
if emitOnlyGenericVariant {
|
|
|
|
// if we only want the generic variant to be emitted
|
|
|
|
// emit it at the root of the package as the default package
|
|
|
|
root = pathPrefix
|
|
|
|
}
|
|
|
|
setFileContent(root, relPath, contents)
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
for _, mod := range pkgMods {
|
|
|
|
pkg := packages[mod]
|
|
|
|
|
|
|
|
// Config, description
|
|
|
|
switch mod {
|
|
|
|
case "":
|
|
|
|
buffer := &bytes.Buffer{}
|
2022-12-08 10:45:46 +00:00
|
|
|
if pkg.pkg.Description() != "" {
|
|
|
|
printComment(buffer, pkg.pkg.Description(), false)
|
2020-04-02 00:22:23 +00:00
|
|
|
} else {
|
2022-08-19 23:24:19 +00:00
|
|
|
fmt.Fprintf(buffer, "// Package %[1]s exports types, functions, subpackages for provisioning %[1]s resources.\n", name)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
fmt.Fprintf(buffer, "package %s\n", name)
|
|
|
|
|
|
|
|
setFile(path.Join(mod, "doc.go"), buffer.String())
|
2023-09-19 10:28:50 +00:00
|
|
|
setGenericVariantFile(path.Join(mod, "doc.go"), buffer.String())
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-06-07 22:34:44 +00:00
|
|
|
// Version
|
|
|
|
versionBuf := &bytes.Buffer{}
|
|
|
|
importsAndAliases := map[string]string{}
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(versionBuf, []string{"github.com/blang/semver"}, importsAndAliases, true /* isUtil */)
|
2023-06-07 22:34:44 +00:00
|
|
|
err = pkg.GenVersionFile(versionBuf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-12-12 12:19:42 +00:00
|
|
|
versionFilePath := pkg.internalModuleName + "/pulumiVersion.go"
|
2023-08-22 17:16:43 +00:00
|
|
|
setFile(path.Join(mod, versionFilePath), versionBuf.String())
|
2023-09-19 10:28:50 +00:00
|
|
|
if emitOnlyGenericVariant {
|
|
|
|
setGenericVariantFile(path.Join(mod, versionFilePath), versionBuf.String())
|
|
|
|
}
|
2023-06-07 22:34:44 +00:00
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
case "config":
|
2022-12-08 10:45:46 +00:00
|
|
|
config, err := pkg.pkg.Config()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(config) > 0 {
|
2020-01-21 22:45:48 +00:00
|
|
|
buffer := &bytes.Buffer{}
|
2022-12-08 10:45:46 +00:00
|
|
|
if err := pkg.genConfig(buffer, config); err != nil {
|
2020-01-21 22:45:48 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
configFilePath := path.Join(mod, "config.go")
|
|
|
|
setFile(configFilePath, buffer.String())
|
|
|
|
setGenericVariantFile(configFilePath, buffer.String())
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resources
|
2023-09-19 10:28:50 +00:00
|
|
|
for _, resource := range pkg.resources {
|
|
|
|
if resource.IsOverlay {
|
2021-11-12 00:00:03 +00:00
|
|
|
// This resource code is generated by the provider, so no further action is required.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-01-15 18:06:57 +00:00
|
|
|
importsAndAliases := map[string]string{}
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.getImports(resource, importsAndAliases)
|
2021-12-10 23:35:24 +00:00
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = ""
|
2023-08-22 17:16:43 +00:00
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, pkg.internalModuleName)] = ""
|
2023-11-04 12:17:41 +00:00
|
|
|
if goPkgInfo.Generics == GenericsSettingSideBySide {
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
buffer := &bytes.Buffer{}
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(buffer, []string{"context", "reflect"}, importsAndAliases, false /* isUtil */)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genResource(
|
|
|
|
buffer,
|
|
|
|
resource,
|
|
|
|
goPkgInfo.GenerateResourceContainerTypes,
|
|
|
|
false /* useGenericVariant */); err != nil {
|
2020-01-21 22:45:48 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
resourceFilePath := path.Join(mod, cgstrings.Camel(rawResourceName(resource))+".go")
|
|
|
|
setFile(resourceFilePath, buffer.String())
|
|
|
|
|
|
|
|
genericVariantBuffer := &bytes.Buffer{}
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
|
|
|
pkg.genHeader(genericVariantBuffer, []string{"context", "reflect"}, importsAndAliases, false /* isUtil */)
|
|
|
|
if err := pkg.genResource(
|
|
|
|
genericVariantBuffer,
|
|
|
|
resource,
|
|
|
|
goPkgInfo.GenerateResourceContainerTypes,
|
|
|
|
true /* useGenericVariant */); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenericVariantFile(resourceFilePath, genericVariantBuffer.String())
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Functions
|
|
|
|
for _, f := range pkg.functions {
|
2021-11-12 00:00:03 +00:00
|
|
|
if f.IsOverlay {
|
|
|
|
// This function code is generated by the provider, so no further action is required.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-10-17 16:37:07 +00:00
|
|
|
fileName := path.Join(mod, cgstrings.Camel(tokenToName(f.Token))+".go")
|
2021-11-23 23:10:15 +00:00
|
|
|
code, err := pkg.genFunctionCodeFile(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-08-23 20:46:09 +00:00
|
|
|
setFile(fileName, code)
|
2023-09-19 10:28:50 +00:00
|
|
|
|
|
|
|
genericCodeVariant, err := pkg.genGenericVariantFunctionCodeFile(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
setGenericVariantFile(fileName, genericCodeVariant)
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
knownTypes := make(map[schema.Type]struct{}, len(pkg.typeDetails))
|
|
|
|
for t := range pkg.typeDetails {
|
|
|
|
knownTypes[t] = struct{}{}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 07:02:56 +00:00
|
|
|
// Enums
|
|
|
|
if len(pkg.enums) > 0 {
|
2021-12-10 23:35:24 +00:00
|
|
|
hasOutputs, imports := false, map[string]string{}
|
2020-11-03 07:02:56 +00:00
|
|
|
for _, e := range pkg.enums {
|
|
|
|
pkg.getImports(e, imports)
|
2021-12-10 23:35:24 +00:00
|
|
|
hasOutputs = hasOutputs || pkg.detailsForType(e).hasOutputs()
|
|
|
|
}
|
|
|
|
var goImports []string
|
|
|
|
if hasOutputs {
|
|
|
|
goImports = []string{"context", "reflect"}
|
|
|
|
imports["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = ""
|
2023-08-28 16:42:37 +00:00
|
|
|
imports["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buffer := &bytes.Buffer{}
|
2023-09-19 10:28:50 +00:00
|
|
|
genericVariantBuffer := &bytes.Buffer{}
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(buffer, goImports, imports, false /* isUtil */)
|
2023-09-19 10:28:50 +00:00
|
|
|
// we do not need any imports for the generic variant
|
|
|
|
pkg.genHeader(genericVariantBuffer, []string{}, map[string]string{}, false /* isUtil */)
|
2020-11-03 07:02:56 +00:00
|
|
|
|
|
|
|
for _, e := range pkg.enums {
|
2023-09-19 10:28:50 +00:00
|
|
|
// generate enums for legacy variant
|
|
|
|
if err := pkg.genEnum(buffer, e, false); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate enums for generic variant
|
|
|
|
if err := pkg.genEnum(genericVariantBuffer, e, true); err != nil {
|
2020-11-18 18:57:01 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-11 05:04:30 +00:00
|
|
|
delete(knownTypes, e)
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
2021-10-14 17:41:40 +00:00
|
|
|
pkg.genEnumRegistrations(buffer)
|
2020-11-03 07:02:56 +00:00
|
|
|
setFile(path.Join(mod, "pulumiEnums.go"), buffer.String())
|
2023-09-19 10:28:50 +00:00
|
|
|
setGenericVariantFile(path.Join(mod, "pulumiEnums.go"), genericVariantBuffer.String())
|
2020-11-03 07:02:56 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
// Types
|
2023-06-28 16:02:04 +00:00
|
|
|
sortedKnownTypes := slice.Prealloc[schema.Type](len(knownTypes))
|
2022-09-12 15:51:35 +00:00
|
|
|
for k := range knownTypes {
|
|
|
|
sortedKnownTypes = append(sortedKnownTypes, k)
|
|
|
|
}
|
|
|
|
sort.Slice(sortedKnownTypes, func(i, j int) bool {
|
|
|
|
return sortedKnownTypes[i].String() < sortedKnownTypes[j].String()
|
|
|
|
})
|
2022-06-21 18:04:13 +00:00
|
|
|
|
2023-12-05 12:29:30 +00:00
|
|
|
if len(pkg.types) == 0 && len(pkg.enums) > 0 {
|
|
|
|
// If there are no types, but there are enums, we still need to generate the types file.
|
|
|
|
// with the associated nested collection enum types such as arrays of enums, maps of enums etc.
|
|
|
|
|
|
|
|
collectionTypes := map[string]*nestedTypeInfo{}
|
|
|
|
for _, t := range sortedKnownTypes {
|
|
|
|
pkg.collectNestedCollectionTypes(collectionTypes, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(collectionTypes) > 0 {
|
|
|
|
buffer := &bytes.Buffer{}
|
|
|
|
useGenericVariant := false
|
|
|
|
err := generateTypes(buffer, pkg, []*schema.ObjectType{}, sortedKnownTypes, useGenericVariant)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
typeFilePath := path.Join(mod, "pulumiTypes.go")
|
|
|
|
setFile(typeFilePath, buffer.String())
|
|
|
|
|
|
|
|
genericVariantBuffer := &bytes.Buffer{}
|
|
|
|
useGenericVariant = true
|
|
|
|
err = generateTypes(genericVariantBuffer, pkg, []*schema.ObjectType{}, sortedKnownTypes, useGenericVariant)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
setGenericVariantFile(typeFilePath, genericVariantBuffer.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-12 15:51:35 +00:00
|
|
|
for types, i := pkg.types, 0; len(types) > 0; i++ {
|
|
|
|
// 500 types corresponds to approximately 5M or 40_000 lines of code.
|
|
|
|
const chunkSize = 500
|
|
|
|
chunk := types
|
|
|
|
if len(chunk) > chunkSize {
|
|
|
|
chunk = chunk[:chunkSize]
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
2022-09-12 15:51:35 +00:00
|
|
|
types = types[len(chunk):]
|
2021-03-11 05:04:30 +00:00
|
|
|
|
2023-03-23 22:16:19 +00:00
|
|
|
// To avoid duplicating collection types into every chunk, only pass known to chunk i=0.
|
|
|
|
known := sortedKnownTypes
|
|
|
|
if i != 0 {
|
|
|
|
known = nil
|
|
|
|
}
|
|
|
|
|
2021-03-11 05:04:30 +00:00
|
|
|
buffer := &bytes.Buffer{}
|
2023-09-19 10:28:50 +00:00
|
|
|
useGenericVariant := false
|
|
|
|
err := generateTypes(buffer, pkg, chunk, known, useGenericVariant)
|
2022-09-12 15:51:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 15:51:35 +00:00
|
|
|
typePath := "pulumiTypes"
|
|
|
|
if i != 0 {
|
|
|
|
typePath = fmt.Sprintf("%s%d", typePath, i)
|
|
|
|
}
|
2023-09-19 10:28:50 +00:00
|
|
|
|
|
|
|
typeFilePath := path.Join(mod, typePath+".go")
|
|
|
|
setFile(typeFilePath, buffer.String())
|
|
|
|
|
|
|
|
genericVariantBuffer := &bytes.Buffer{}
|
|
|
|
useGenericVariant = true
|
|
|
|
err = generateTypes(genericVariantBuffer, pkg, chunk, known, useGenericVariant)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenericVariantFile(typeFilePath, genericVariantBuffer.String())
|
2021-03-11 05:04:30 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 22:45:48 +00:00
|
|
|
// Utilities
|
2023-06-12 23:23:54 +00:00
|
|
|
if len(mod) == 0 {
|
2020-01-21 22:45:48 +00:00
|
|
|
buffer := &bytes.Buffer{}
|
2021-01-15 18:06:57 +00:00
|
|
|
importsAndAliases := map[string]string{
|
|
|
|
"github.com/blang/semver": "",
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi": "",
|
2021-01-15 18:06:57 +00:00
|
|
|
}
|
2023-06-13 18:48:34 +00:00
|
|
|
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(buffer, []string{"fmt", "os", "reflect", "regexp", "strconv", "strings"}, importsAndAliases, true /* isUtil */)
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
packageRegex := fmt.Sprintf("^.*/pulumi-%s/sdk(/v\\d+)?", pkg.pkg.Name())
|
2021-04-29 23:09:05 +00:00
|
|
|
if pkg.rootPackageName != "" {
|
|
|
|
packageRegex = fmt.Sprintf("^%s(/v\\d+)?", pkg.importBasePath)
|
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
err := pkg.GenUtilitiesFile(buffer, packageRegex)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
|
2023-12-12 12:19:42 +00:00
|
|
|
utilFilePath := pkg.internalModuleName + "/pulumiUtilities.go"
|
2023-08-22 17:16:43 +00:00
|
|
|
setFile(path.Join(mod, utilFilePath), buffer.String())
|
2023-09-19 10:28:50 +00:00
|
|
|
if emitOnlyGenericVariant {
|
|
|
|
setGenericVariantFile(path.Join(mod, utilFilePath), buffer.String())
|
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
2021-01-19 23:59:51 +00:00
|
|
|
|
|
|
|
// If there are resources in this module, register the module with the runtime.
|
2021-11-12 00:00:03 +00:00
|
|
|
if len(pkg.resources) != 0 && !allResourcesAreOverlays(pkg.resources) {
|
2021-01-19 23:59:51 +00:00
|
|
|
buffer := &bytes.Buffer{}
|
2022-12-08 10:45:46 +00:00
|
|
|
err := pkg.genResourceModule(buffer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-19 23:59:51 +00:00
|
|
|
|
|
|
|
setFile(path.Join(mod, "init.go"), buffer.String())
|
2023-09-19 10:28:50 +00:00
|
|
|
|
|
|
|
genericVariantBuffer := &bytes.Buffer{}
|
|
|
|
if err := pkg.genResourceModule(genericVariantBuffer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenericVariantFile(path.Join(mod, "init.go"), genericVariantBuffer.String())
|
2021-01-19 23:59:51 +00:00
|
|
|
}
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return files, nil
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
func generateTypes(
|
|
|
|
w io.Writer,
|
|
|
|
pkg *pkgContext,
|
|
|
|
types []*schema.ObjectType,
|
|
|
|
knownTypes []schema.Type,
|
|
|
|
useGenericTypes bool,
|
|
|
|
) error {
|
2022-09-12 15:51:35 +00:00
|
|
|
hasOutputs, importsAndAliases := false, map[string]string{}
|
|
|
|
for _, t := range types {
|
|
|
|
pkg.getImports(t, importsAndAliases)
|
|
|
|
hasOutputs = hasOutputs || pkg.detailsForType(t).hasOutputs()
|
|
|
|
}
|
|
|
|
|
|
|
|
collectionTypes := map[string]*nestedTypeInfo{}
|
|
|
|
for _, t := range knownTypes {
|
|
|
|
pkg.collectNestedCollectionTypes(collectionTypes, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// All collection types have Outputs
|
|
|
|
if len(collectionTypes) > 0 {
|
|
|
|
hasOutputs = true
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:17:41 +00:00
|
|
|
goInfo := goPackageInfo(pkg.pkg)
|
2022-09-12 15:51:35 +00:00
|
|
|
var goImports []string
|
|
|
|
if hasOutputs {
|
|
|
|
goImports = []string{"context", "reflect"}
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = ""
|
2023-11-04 12:17:41 +00:00
|
|
|
if goInfo.Generics == GenericsSettingSideBySide {
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
|
|
|
}
|
2022-09-12 15:51:35 +00:00
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
if useGenericTypes && hasOutputs {
|
|
|
|
importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumix"] = ""
|
|
|
|
}
|
|
|
|
|
2023-08-22 17:16:43 +00:00
|
|
|
importsAndAliases[path.Join(pkg.importBasePath, pkg.internalModuleName)] = ""
|
2023-07-06 20:20:04 +00:00
|
|
|
pkg.genHeader(w, goImports, importsAndAliases, false /* isUtil */)
|
2023-06-13 18:48:34 +00:00
|
|
|
// in case we're not using the internal package, assign to a blank var
|
2023-08-22 17:16:43 +00:00
|
|
|
fmt.Fprintf(w, "var _ = %s.GetEnvOrDefault\n", pkg.internalModuleName)
|
2022-09-12 15:51:35 +00:00
|
|
|
|
|
|
|
for _, t := range types {
|
2023-09-19 10:28:50 +00:00
|
|
|
if err := pkg.genType(w, t, useGenericTypes); err != nil {
|
2022-09-12 15:51:35 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
typeNames := []string{}
|
|
|
|
if !useGenericTypes {
|
|
|
|
typeNames = pkg.genNestedCollectionTypes(w, collectionTypes)
|
|
|
|
}
|
2022-09-12 15:51:35 +00:00
|
|
|
|
2023-09-19 10:28:50 +00:00
|
|
|
pkg.genTypeRegistrations(w, types, useGenericTypes, typeNames...)
|
2022-09-12 15:51:35 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-12 00:00:03 +00:00
|
|
|
func allResourcesAreOverlays(resources []*schema.Resource) bool {
|
|
|
|
for _, r := range resources {
|
|
|
|
if !r.IsOverlay {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-09-15 11:56:58 +00:00
|
|
|
// goPackage returns the suggested package name for the given string.
|
|
|
|
func goPackage(name string) string {
|
2021-10-11 22:28:11 +00:00
|
|
|
return strings.ReplaceAll(name, "-", "")
|
2020-09-15 11:56:58 +00:00
|
|
|
}
|
|
|
|
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
//go:embed embeddedUtilities.go
|
|
|
|
var embeddedUtilities string
|
2020-01-21 22:45:48 +00:00
|
|
|
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
func (pkg *pkgContext) GenUtilitiesFile(w io.Writer, packageRegex string) error {
|
|
|
|
subtitutions := map[string]string{
|
|
|
|
`"${packageRegex}"`: fmt.Sprintf("%q", packageRegex),
|
2020-01-21 22:45:48 +00:00
|
|
|
}
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
i := strings.Index(embeddedUtilities, "package utilities")
|
|
|
|
code := embeddedUtilities[i+len("package utilities"):]
|
|
|
|
for x, y := range subtitutions {
|
|
|
|
code = strings.ReplaceAll(code, x, y)
|
2021-11-23 23:10:15 +00:00
|
|
|
}
|
Support returning plain values from methods (#13592)
Support returning plain values from methods.
Implements Node, Python and Go support.
Remaining:
- [x] test receiving unknowns
- [x] acceptance tests written and passing locally for Node, Python, Go
clients against a Go server
- [x] acceptance tests passing in CI
- [x] tickets filed for remaining languages
- [x] https://github.com/pulumi/pulumi-yaml/issues/499
- [x] https://github.com/pulumi/pulumi-java/issues/1193
- [x] https://github.com/pulumi/pulumi-dotnet/issues/170
Known limitations:
- this is technically a breaking change in case there is code out there
that already uses methods that return Plain: true
- struct-wrapping limitation: the provider for the component resource
needs to still wrap the plain-returning Method response with a 1-arg
struct; by convention the field is named "res", and this is how it
travels through the plumbing
- resources cannot return plain values yet
- the provider for the component resource cannot have unknown
configuration, if it does, the methods will not be called
- Per Luke https://github.com/pulumi/pulumi/issues/11520 this might not
be supported/realizable yet
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
Fixes https://github.com/pulumi/pulumi/issues/12709
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [ ] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-11-18 06:02:06 +00:00
|
|
|
_, err := fmt.Fprintf(w, "%s", code)
|
2022-12-08 10:45:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return pkg.GenPkgDefaultOpts(w)
|
2022-01-11 01:38:54 +00:00
|
|
|
}
|
|
|
|
|
2023-06-07 22:34:44 +00:00
|
|
|
func (pkg *pkgContext) GenVersionFile(w io.Writer) error {
|
|
|
|
const versionFile = `var SdkVersion semver.Version = semver.Version{}
|
2023-06-08 19:15:55 +00:00
|
|
|
var pluginDownloadURL string = ""
|
2023-06-07 22:34:44 +00:00
|
|
|
`
|
2023-06-08 23:30:05 +00:00
|
|
|
_, err := fmt.Fprint(w, versionFile)
|
2023-06-07 22:34:44 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
func (pkg *pkgContext) GenPkgDefaultOpts(w io.Writer) error {
|
|
|
|
p, err := pkg.pkg.Definition()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
url := p.PluginDownloadURL
|
2022-01-11 01:38:54 +00:00
|
|
|
const template string = `
|
2023-06-09 23:57:30 +00:00
|
|
|
// Pkg%[1]sDefaultOpts provides package level defaults to pulumi.Option%[1]s.
|
|
|
|
func Pkg%[1]sDefaultOpts(opts []pulumi.%[1]sOption) []pulumi.%[1]sOption {
|
2023-06-08 22:58:39 +00:00
|
|
|
defaults := []pulumi.%[1]sOption{}
|
|
|
|
%[2]s
|
|
|
|
version := %[3]s
|
|
|
|
if !version.Equals(semver.Version{}){
|
|
|
|
defaults = append(defaults, pulumi.Version(version.String()))
|
|
|
|
}
|
2022-01-11 01:38:54 +00:00
|
|
|
return append(defaults, opts...)
|
|
|
|
}
|
|
|
|
`
|
2023-06-08 22:58:39 +00:00
|
|
|
var pluginDownloadURL string
|
|
|
|
if url != "" {
|
|
|
|
pluginDownloadURL = fmt.Sprintf(`defaults = append(defaults, pulumi.PluginDownloadURL("%s"))`, url)
|
|
|
|
}
|
|
|
|
|
2023-06-08 19:15:55 +00:00
|
|
|
versionPackageRef := "SdkVersion"
|
2023-06-09 23:09:22 +00:00
|
|
|
|
|
|
|
versionPkgName := strings.ReplaceAll(pkg.pkg.Name(), "-", "")
|
2023-06-08 19:15:55 +00:00
|
|
|
|
2023-06-08 22:58:39 +00:00
|
|
|
if pkg.mod != "" {
|
2023-06-08 19:15:55 +00:00
|
|
|
versionPackageRef = versionPkgName + "." + versionPackageRef
|
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
if info := p.Language["go"]; info != nil {
|
|
|
|
if info.(GoPackageInfo).RespectSchemaVersion && pkg.pkg.Version() != nil {
|
2023-06-08 22:58:39 +00:00
|
|
|
versionPackageRef = fmt.Sprintf("semver.MustParse(%q)", p.Version.String())
|
2022-06-08 19:03:17 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-11 01:38:54 +00:00
|
|
|
for _, typ := range []string{"Resource", "Invoke"} {
|
2023-06-08 22:58:39 +00:00
|
|
|
_, err := fmt.Fprintf(w, template, typ, pluginDownloadURL, versionPackageRef)
|
2022-12-08 10:45:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-01-11 01:38:54 +00:00
|
|
|
}
|
2022-12-08 10:45:46 +00:00
|
|
|
return nil
|
2022-01-11 01:38:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GenPkgDefaultsOptsCall generates a call to Pkg{TYPE}DefaultsOpts.
|
2022-12-08 10:45:46 +00:00
|
|
|
func (pkg *pkgContext) GenPkgDefaultsOptsCall(w io.Writer, invoke bool) error {
|
2022-01-11 01:38:54 +00:00
|
|
|
typ := "Resource"
|
|
|
|
if invoke {
|
|
|
|
typ = "Invoke"
|
|
|
|
}
|
2023-06-12 23:23:54 +00:00
|
|
|
|
2023-08-22 17:16:43 +00:00
|
|
|
_, err := fmt.Fprintf(w, "\topts = %s.Pkg%sDefaultOpts(opts)\n", pkg.internalModuleName, typ)
|
2023-06-09 23:57:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-06-12 23:23:54 +00:00
|
|
|
|
2023-06-09 23:57:30 +00:00
|
|
|
return nil
|
2022-01-11 01:38:54 +00:00
|
|
|
}
|